.claude/skills/blender-web-pipeline/SKILL.md
Blender to web export workflows for 3D models and animations. Use this skill when exporting Blender models to glTF for web, optimizing 3D assets for Three.js or Babylon.js, batch processing models with Python scripts, automating Blender workflows, or creating web-ready 3D pipelines. Triggers on tasks involving Blender glTF export, bpy scripting, 3D asset optimization, model compression, texture baking, or Blender automation. Exports models for threejs-webgl, react-three-fiber, and babylonjs-engine skills.
npx skillsauth add freshtechbro/claudedesignskills blender-web-pipelineInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Blender Web Pipeline skill provides workflows for exporting 3D models and animations from Blender to web-optimized formats (primarily glTF 2.0). It covers Python scripting for batch processing, optimization techniques for web performance, and integration with web 3D libraries like Three.js and Babylon.js.
When to use this skill:
Key capabilities:
Why glTF for Web:
glTF vs GLB:
.gltf = JSON + external .bin + external textures
.glb = Single binary file (recommended for web)
Access Blender data and operations via Python:
import bpy
# Access scene data
scene = bpy.context.scene
objects = bpy.data.objects
# Modify objects
obj = bpy.data.objects['Cube']
obj.location = (0, 0, 1)
obj.scale = (2, 2, 2)
# Export glTF
bpy.ops.export_scene.gltf(
filepath='/path/to/model.glb',
export_format='GLB'
)
Target Metrics:
# Blender Python Console or script
import bpy
# Select objects to export (optional - exports all if none selected)
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects['MyModel'].select_set(True)
# Export as GLB
bpy.ops.export_scene.gltf(
filepath='/path/to/output.glb',
export_format='GLB', # Binary format
use_selection=True, # Export selected only
export_apply=True, # Apply modifiers
export_texcoords=True, # UV coordinates
export_normals=True, # Normals
export_materials='EXPORT', # Export materials
export_colors=True, # Vertex colors
export_cameras=False, # Skip cameras
export_lights=False, # Skip lights
export_animations=True, # Include animations
export_draco_mesh_compression_enable=True, # Compress geometry
export_draco_mesh_compression_level=6, # 0-10 (6 recommended)
export_draco_position_quantization=14, # 8-14 bits
export_draco_normal_quantization=10, # 8-10 bits
export_draco_texcoord_quantization=12 # 8-12 bits
)
#!/usr/bin/env blender --background --python
"""
Batch export all .blend files in a directory to glTF
Usage: blender --background --python batch_export.py -- /path/to/blend/files
"""
import bpy
import os
import sys
# Get command line arguments after --
argv = sys.argv
argv = argv[argv.index("--") + 1:] if "--" in argv else []
input_dir = argv[0] if argv else "/path/to/models"
output_dir = argv[1] if len(argv) > 1 else input_dir + "_gltf"
# Create output directory
os.makedirs(output_dir, exist_ok=True)
# Find all .blend files
blend_files = [f for f in os.listdir(input_dir) if f.endswith('.blend')]
print(f"Found {len(blend_files)} .blend files")
for blend_file in blend_files:
input_path = os.path.join(input_dir, blend_file)
output_name = blend_file.replace('.blend', '.glb')
output_path = os.path.join(output_dir, output_name)
print(f"Processing: {blend_file}")
# Open blend file
bpy.ops.wm.open_mainfile(filepath=input_path)
# Export as GLB with optimizations
bpy.ops.export_scene.gltf(
filepath=output_path,
export_format='GLB',
export_apply=True,
export_draco_mesh_compression_enable=True,
export_draco_mesh_compression_level=6
)
print(f" Exported: {output_name}")
print("Batch export complete!")
Run batch script:
blender --background --python batch_export.py -- /models/source /models/output
import bpy
def optimize_mesh(obj, target_ratio=0.5):
"""Reduce polygon count using decimation modifier."""
if obj.type != 'MESH':
return
# Add Decimate modifier
decimate = obj.modifiers.new(name='Decimate', type='DECIMATE')
decimate.ratio = target_ratio # 0.5 = 50% of original polygons
decimate.use_collapse_triangulate = True
# Apply modifier
bpy.context.view_layer.objects.active = obj
bpy.ops.object.modifier_apply(modifier='Decimate')
print(f"Optimized {obj.name}: {len(obj.data.polygons)} polygons")
# Optimize all selected meshes
for obj in bpy.context.selected_objects:
optimize_mesh(obj, target_ratio=0.3)
import bpy
def bake_textures(obj, resolution=1024):
"""Bake all materials to single texture."""
# Setup bake settings
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.bake_type = 'COMBINED'
# Create bake image
bake_image = bpy.data.images.new(
name=f"{obj.name}_bake",
width=resolution,
height=resolution
)
# Create bake material
mat = bpy.data.materials.new(name=f"{obj.name}_baked")
mat.use_nodes = True
nodes = mat.node_tree.nodes
# Add Image Texture node
tex_node = nodes.new(type='ShaderNodeTexImage')
tex_node.image = bake_image
tex_node.select = True
nodes.active = tex_node
# Assign material
if obj.data.materials:
obj.data.materials[0] = mat
else:
obj.data.materials.append(mat)
# Select object
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
# Bake
bpy.ops.object.bake(type='COMBINED')
# Save baked texture
bake_image.filepath_raw = f"/tmp/{obj.name}_bake.png"
bake_image.file_format = 'PNG'
bake_image.save()
print(f"Baked {obj.name} to {bake_image.filepath_raw}")
# Bake selected objects
for obj in bpy.context.selected_objects:
if obj.type == 'MESH':
bake_textures(obj, resolution=2048)
import bpy
def generate_lods(obj, lod_levels=[0.75, 0.5, 0.25]):
"""Generate LOD copies with decreasing polygon counts."""
lod_objects = []
for i, ratio in enumerate(lod_levels):
# Duplicate object
lod_obj = obj.copy()
lod_obj.data = obj.data.copy()
lod_obj.name = f"{obj.name}_LOD{i}"
# Link to scene
bpy.context.collection.objects.link(lod_obj)
# Add Decimate modifier
decimate = lod_obj.modifiers.new(name='Decimate', type='DECIMATE')
decimate.ratio = ratio
# Apply modifier
bpy.context.view_layer.objects.active = lod_obj
bpy.ops.object.modifier_apply(modifier='Decimate')
lod_objects.append(lod_obj)
print(f"Created {lod_obj.name}: {len(lod_obj.data.polygons)} polygons")
return lod_objects
# Generate LODs for selected object
if bpy.context.active_object:
generate_lods(bpy.context.active_object)
import bpy
import os
def export_optimized_gltf(filepath, texture_max_size=1024):
"""Export glTF with downscaled textures."""
# Downscale all textures
for img in bpy.data.images:
if img.size[0] > texture_max_size or img.size[1] > texture_max_size:
img.scale(texture_max_size, texture_max_size)
print(f"Downscaled {img.name} to {texture_max_size}x{texture_max_size}")
# Export with Draco compression
bpy.ops.export_scene.gltf(
filepath=filepath,
export_format='GLB',
export_apply=True,
export_image_format='JPEG', # JPEG for smaller size (or PNG for quality)
export_jpeg_quality=85, # 0-100
export_draco_mesh_compression_enable=True,
export_draco_mesh_compression_level=8, # Max compression
export_draco_position_quantization=12,
export_draco_normal_quantization=8,
export_draco_texcoord_quantization=10
)
# Export optimized
export_optimized_gltf('/path/to/optimized.glb', texture_max_size=512)
#!/bin/bash
# Batch export Blender files to glTF without opening GUI
SCRIPT_DIR="$(dirname "$0")"
# Export all .blend files in current directory
for blend_file in *.blend; do
echo "Exporting $blend_file..."
blender --background "$blend_file" --python - <<EOF
import bpy
import os
# Get output filename
filename = os.path.splitext(bpy.data.filepath)[0]
output = filename + '.glb'
# Export
bpy.ops.export_scene.gltf(
filepath=output,
export_format='GLB',
export_apply=True,
export_draco_mesh_compression_enable=True,
export_draco_mesh_compression_level=6
)
print(f'Exported to {output}')
EOF
done
echo "All files exported!"
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
const loader = new GLTFLoader();
// Setup Draco decoder for compressed models
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/');
loader.setDRACOLoader(dracoLoader);
// Load Blender export
loader.load('/models/exported.glb', (gltf) => {
scene.add(gltf.scene);
// Play animations
if (gltf.animations.length > 0) {
const mixer = new THREE.AnimationMixer(gltf.scene);
const action = mixer.clipAction(gltf.animations[0]);
action.play();
}
});
import { useGLTF } from '@react-three/drei';
function Model() {
const { scene } = useGLTF('/models/exported.glb');
return <primitive object={scene} />;
}
// Preload for better performance
useGLTF.preload('/models/exported.glb');
import * as BABYLON from '@babylonjs/core';
import '@babylonjs/loaders/glTF';
BABYLON.SceneLoader.ImportMesh(
'',
'/models/',
'exported.glb',
scene,
(meshes) => {
console.log('Loaded meshes:', meshes);
}
);
Decimate Modifier:
# Reduce polygon count by 70%
obj.modifiers.new(name='Decimate', type='DECIMATE')
obj.modifiers['Decimate'].ratio = 0.3
Merge by Distance:
# Remove duplicate vertices
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.remove_doubles(threshold=0.0001)
bpy.ops.object.mode_set(mode='OBJECT')
Triangulate Faces:
# Ensure all faces are triangles (required for some engines)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris()
bpy.ops.object.mode_set(mode='OBJECT')
Image Compression:
# Save textures as JPEG (lossy but smaller)
for img in bpy.data.images:
img.file_format = 'JPEG'
img.filepath_raw = f"/output/{img.name}.jpg"
img.save()
Texture Atlas:
# Combine multiple textures into one atlas
# Use Smart UV Project for automatic atlasing
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.uv.smart_project(angle_limit=66, island_margin=0.02)
bpy.ops.object.mode_set(mode='OBJECT')
Convert to PBR:
# Ensure materials use Principled BSDF (glTF standard)
for mat in bpy.data.materials:
if not mat.use_nodes:
mat.use_nodes = True
nodes = mat.node_tree.nodes
principled = nodes.get('Principled BSDF')
if not principled:
principled = nodes.new(type='ShaderNodeBsdfPrincipled')
output = nodes.get('Material Output')
mat.node_tree.links.new(principled.outputs[0], output.inputs[0])
Problem: Exported .glb files are 20+ MB
Solutions:
Problem: Textures don't appear in web viewer
Solutions:
Problem: Animations don't export or play incorrectly
Solutions:
Problem: Materials render differently in web vs Blender
Solutions:
Problem: Export takes 10+ minutes
Solutions:
Problem: Model lags in browser
Solutions:
☐ Apply all modifiers
☐ Merge vertices (remove doubles)
☐ Triangulate faces (if required)
☐ Optimize polygon count (<50k triangles)
☐ UV unwrap all meshes
☐ Bake materials (if complex)
☐ Resize textures (max 2048x2048)
☐ Use Principled BSDF materials
☐ Remove unused data (orphan cleanup)
☐ Name objects descriptively
☐ Set origin points correctly
☐ Apply transformations (Ctrl+A)
# Recommended glTF export settings
bpy.ops.export_scene.gltf(
filepath='/output.glb',
export_format='GLB', # Binary format
export_apply=True, # Apply modifiers
export_image_format='JPEG', # Smaller file size
export_jpeg_quality=85, # Quality vs size
export_draco_mesh_compression_enable=True, # Enable compression
export_draco_mesh_compression_level=6, # Balance speed/size
export_animations=True, # Include animations
export_lights=False, # Skip lights (recreate in code)
export_cameras=False # Skip cameras
)
This skill includes:
batch_export.py - Batch export .blend files to glTFoptimize_model.py - Optimize geometry and textures for webgenerate_lods.py - Generate LOD copies automaticallygltf_export_guide.md - Complete glTF export referencebpy_api_reference.md - Blender Python API quick referenceoptimization_strategies.md - Detailed optimization techniquesexport_template.blend - Pre-configured export templateshader_library/ - Web-optimized PBR shadersdevelopment
Meta-skill for combining Three.js, GSAP ScrollTrigger, React Three Fiber, Motion, and React Spring for complex 3D web experiences. Use when building applications that integrate multiple 3D and animation libraries, requiring architecture patterns, state management, and performance optimization across the stack. Triggers on tasks involving library integration, multi-library architectures, scroll-driven 3D experiences, physics-based 3D animations, or complex interactive 3D applications.
development
Comprehensive skill for Three.js 3D web development. Use this skill when building interactive 3D scenes, WebGL/WebGPU applications, product configurators, 3D visualizations, or immersive web experiences. Triggers on tasks involving Three.js, 3D rendering, scenes, cameras, meshes, materials, lights, animations, textures, or WebGL/WebGPU rendering.
tools
Comprehensive skill for Adobe Substance 3D Painter texturing and material creation workflow. Use this skill when creating PBR materials, exporting textures for web/game engines, optimizing 3D assets for real-time rendering, or automating texture workflows. Triggers on tasks involving Substance 3D Painter, PBR texturing, material creation, texture export for Three.js, Babylon.js, Unity, Unreal, glTF optimization, or Python API automation. Creates optimized textures for threejs-webgl, react-three-fiber, and babylonjs-engine materials.
tools
Browser-based 3D design tool with visual editor, animation, and web export. Use this skill when creating 3D scenes without code, designing interactive web experiences, prototyping 3D UI, exporting to React/web, or building designer-friendly 3D content. Triggers on tasks involving Spline, no-code 3D, visual 3D editor, 3D animation, state-based interactions, React Spline integration, or scene export. Alternative to Three.js for designers who prefer visual tools over code.