Generate Animated Rings With Blender Python API For Website

No More Manual Rendering!
No More Manual Rendering!

Live stream set for 2025-12-08 at 14:00:00 Eastern

Ask questions in the live chat about any programming or lifestyle topic.

This livestream will be on YouTube or you can watch below.


Generate Animated 3D Rings for the Web with Blender Python and model-viewer

Do you want to bring animated 3D models into your website without being a rendering expert? This post walks you through generating a stunning low poly animated ring model using **Blender’s Python API** and displaying it seamlessly on your blog using the Google **model-viewer** web component.

We will use a reliable complete Python script to automate the entire process overcoming common scripting hurdles in Blender’s background mode.

1. The Blender Python Script

The following script automates the creation of three glossy animated rings sets up the camera and lighting and exports the final ready for web **GLB** file. We use simple keyframes for stability meaning the animation will loop although with a slight ease in/ease out effect Bezier interpolation due to a known Blender bug in this specific workflow.

import bpy
import math
import os

# --- Configuration ---
BLENDER_FILE_NAME = "low-poly-animated-rings.blend"
EXPORT_FILE_NAME = "low-poly-animated-rings.glb"
# Determine script directory for saving/exporting files
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) if '__file__' in globals() else (os.path.dirname(bpy.data.filepath) if bpy.data.filepath else os.getcwd())

# Geometry and Animation Settings
LOW_POLY_VERTICES = 16 
RING_THICKNESS = 0.3
ANIMATION_FRAMES = 60
FULL_ROTATION = 2 * math.pi

# Ring Data: (Name, Major Radius, Material Color RGB, Axis)
rings_data = [
    ("Ring_Red", 4.0, (0.8, 0.1, 0.1), 'X'),
    ("Ring_Green", 3.0, (0.1, 0.8, 0.1), 'Y'),
    ("Ring_Blue", 2.0, (0.1, 0.1, 0.8), 'Z'),
]

# --- Utility Functions ---

def clear_scene():
    """Removes all objects from the current scene."""
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete(use_global=False)

def create_glossy_material(name, color_rgb):
    """Creates a glossy metallic material."""
    if name in bpy.data.materials: return bpy.data.materials[name]
    mat = bpy.data.materials.new(name=name)
    mat.use_nodes = True
    
    bsdf = mat.node_tree.nodes["Principled BSDF"]
    bsdf.inputs['Base Color'].default_value = color_rgb + (1.0,)
    bsdf.inputs['Roughness'].default_value = 0.1
    bsdf.inputs['Metallic'].default_value = 0.8
    return mat

def apply_material(obj, mat):
    """Applies a material to a given object."""
    if obj.data.materials: obj.data.materials[0] = mat
    else: obj.data.materials.append(mat)

# --- Main Generation Logic ---

def generate_animated_rings():
    clear_scene()
    ring_objects = []

    # --- 1. Create Geometry and Materials ---
    
    for name, radius, color_rgb, axis in rings_data:
        mat = create_glossy_material(f"{name}_Mat", color_rgb)
        
        bpy.ops.mesh.primitive_torus_add(
            major_segments=LOW_POLY_VERTICES, 
            minor_segments=LOW_POLY_VERTICES,
            major_radius=radius, 
            minor_radius=RING_THICKNESS, 
            align='WORLD', 
            location=(0, 0, 0)
        )
        ring = bpy.context.object
        ring.name = name
        apply_material(ring, mat)
        
        ring_objects.append({'obj': ring, 'axis': axis})

    # --- 2. Animate Rotation (Simple Keyframes Only) ---
    
    bpy.context.scene.frame_start = 1
    bpy.context.scene.frame_end = ANIMATION_FRAMES
    
    for ring_info in ring_objects:
        ring = ring_info['obj']
        axis = ring_info['axis']
        
        if not ring.animation_data:
            ring.animation_data_create()
        
        # Keyframe 1: Start rotation (Frame 1)
        bpy.context.scene.frame_set(1)
        ring.rotation_euler.x = 0
        ring.rotation_euler.y = 0
        ring.rotation_euler.z = 0
        ring.keyframe_insert(data_path="rotation_euler", frame=1)
        
        # Keyframe 2: Full rotation (Frame ANIMATION_FRAMES)
        bpy.context.scene.frame_set(ANIMATION_FRAMES)
        
        # Set rotation for the specific axis
        if axis == 'X':
            ring.rotation_euler.x = FULL_ROTATION
        elif axis == 'Y':
            ring.rotation_euler.y = FULL_ROTATION
        else: # 'Z'
            ring.rotation_euler.z = FULL_ROTATION
            
        ring.keyframe_insert(data_path="rotation_euler", frame=ANIMATION_FRAMES)

    # --- 3. Add Camera and Light ---
    
    # Camera setup
    bpy.ops.object.camera_add(location=(0, -8, 2), rotation=(math.radians(70), 0, 0))
    bpy.context.scene.camera = bpy.context.object

    # Light setup (Confirmed working fix)
    bpy.ops.object.light_add(type='POINT', location=(5, -5, 5))
    light_object = bpy.context.object
    light_object.data.energy = 1000.0


    # --- 4. Save and Export ---
    
    blend_save_path = os.path.join(SCRIPT_DIR, BLENDER_FILE_NAME)
    output_path = os.path.join(SCRIPT_DIR, EXPORT_FILE_NAME)

    # Save .blend file
    try:
        bpy.ops.wm.save_as_mainfile(filepath=blend_save_path)
        print(f"INFO: Successfully saved .blend file to: {blend_save_path}")
    except RuntimeError as e:
        print(f"WARNING: Failed to save .blend file. Reason: {e}")

    # Export to GLB
    bpy.ops.object.select_all(action='SELECT') # Select all objects for export
    
    try:
        bpy.ops.export_scene.gltf(
            filepath=output_path,
            export_format='GLB',
            export_apply=True,
            export_yup=True,
            export_animations=True, # Exports the rotation animation
            use_selection=True
        )
        print(f"INFO: Successfully exported GLB file to: {output_path}")
    except RuntimeError as e:
        print(f"ERROR: Failed to export GLB file. Reason: {e}")


# --- Execution ---

if __name__ == "__main__":
    generate_animated_rings()
    print(f"\n--- Animated Rings Generation Complete. \u2705 ---")

2. How to Run the Python Script

To execute this script you must run **Blender** from your computers command line Terminal on macOS/Linux or Command Prompt/PowerShell on Windows.

Running the Command

  1. Save the Code: Save the script above as a file named low_poly_rings.py.
  2. Open Terminal/Command Prompt.
  3. Run the command: Execute Blender in background mode (-b) and tell it to run your script (-P). Replace the paths below with the actual location of your Blender executable and your script.
/path/to/blender -b -P /path/to/low_poly_rings.py

Upon successful completion this command will save two files in the same directory as your script:

  • low-poly-animated-rings.blend
  • low-poly-animated-rings.glb (The file you will use on the web)

3. Lighting Your Scene with HDR Images

In 3D graphics especially for realistic rendering we use **Image Based Lighting IBL** to illuminate the scene using high dynamic range images. This allows shiny objects like our glossy rings to reflect the environment naturally.

Your rings are lit by the **Courtyard EXR** file which is a standard High Dynamic Range Imaging HDRI texture shipped with **Blender 5.0 LTS**. This image provides natural lighting and realistic reflections for the glossy materials defined in the script.

4. Web Browser Support for Image Formats

When deciding which HDRI or texture format to use on a website **browser support** is crucial. Here is a comparison of the mentioned formats and their web support:

Format Type Web Browser Support Open Source Alternative
.exr HDR Poor (Requires processing/conversion) .hdr (Radiance)
.hdr HDR Poor (Requires processing/conversion) .hdr (Radiance)
.ktx Texture Good (Supported via WebGL/WebGPU) .ktx (Open Standard)
.ktx2 Texture Excellent (Modern Standard via WebGL 2/WebGPU) .ktx2 (Open Standard)

Conclusion: For maximum modern browser support of animated 3D models and textures **.glb** which includes textures is the best choice and textures within the .glb or for IBL should ideally use formats supported by **WebGPU/WebGL** like **.ktx2**. The **.exr** file must be processed server side or by the model-viewer component before display.

5. Displaying the Rings with model-viewer

The easiest way to embed your animated .glb file into a blog post is by using the **model-viewer** component. It handles lighting controls and animation automatically.

  1. Add the script tag to the head of your HTML or blog post template:
    <script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.5.0/model-viewer.min.js"></script>
  2. Insert the component: Use the tag pointing to your exported .glb file.
    <model-viewer
        src="/path/to/low-poly-animated-rings.glb"
        alt="Three animated low-poly rings"
        shadow-intensity="1"
        camera-controls
        auto-rotate
        exposure="1.5"
        animation-name="default"
        autoplay
        loop
        style="width: 100%; height: 500px;"
    >
    </model-viewer>
    

📸 Screenshots & Screencast

Low poly animated-rings Python code
Blender Scripting Workspace Displaying Low Poly Animated Rings Python Code

Low poly animated-rings in Blender
Blender Layout Workspace Displaying Low Poly Animated Rings

Low poly animated-rings in Web browser
Web Browser Displaying Rendered Low Poly Animated Rings

Screencast For Blender Python API Low Poly Animated Rings

About the Author

If you are looking to deepen your Python and Blender skills check out my resources:

Recommended Resources:

Disclosure: Some of the links above are referral (affiliate) links. I may earn a commission if you purchase through them - at no extra cost to you.

About Edward

Edward is a software engineer, web developer, and author dedicated to helping people achieve their personal and professional goals through actionable advice and real-world tools.

As the author of impactful books including Learning JavaScript, Learning Python, Learning PHP, Mastering Blender Python API, and fiction The Algorithmic Serpent, Edward writes with a focus on personal growth, entrepreneurship, and practical success strategies. His work is designed to guide, motivate, and empower.

In addition to writing, Edward offers professional "full-stack development," "database design," "1-on-1 tutoring," "consulting sessions,", tailored to help you take the next step. Whether you are launching a business, developing a brand, or leveling up your mindset, Edward will be there to support you.

Edward also offers online courses designed to deepen your learning and accelerate your progress. Explore the programming on languages like JavaScript, Python and PHP to find the perfect fit for your journey.

📚 Explore His Books – Visit the Book Shop to grab your copies today.
💼 Need Support? – Learn more about Services and the ways to benefit from his expertise.
🎓 Ready to Learn? – Check out his Online Courses to turn your ideas into results.

Leave a Reply

Your email address will not be published. Required fields are marked *