Spaces:
Running on Zero
A newer version of the Gradio SDK is available: 6.19.0
title: ThreeGen
emoji: π§
colorFrom: blue
colorTo: purple
sdk: gradio
sdk_version: 5.29.0
app_file: app.py
pinned: false
tags:
- build-small
- off-brand
- tiny-titan
- best-demo
- track:wood
- sponsor:openai
- achievement:offbrand
- achievement:fieldnotes
π§ ThreeGen
Describe a 3D scene, get clean Three.js code.
Type a prompt. A 3B model emits a JSON scene graph. A deterministic compiler turns it into a live Three.js preview with copy-paste-ready code.
What it does
ThreeGen converts natural-language prompts into interactive 3D scenes. You describe an object β a star badge, a stack of neon cubes, a glass torus knot β and get a self-contained .html file you can open in any browser. The preview in the app is the exact same code that ends up in the Code tab: no divergence between "preview" and "output."
The problem and the approach
Small models can't write Three.js directly β they hallucinate method names, invent APIs, and produce broken code at high rates. ThreeGen sidesteps this by splitting the job in two:
- The model writes structured JSON β a narrow, validated scene graph (objects, materials, layout, animations). This is a small, learnable format even a 3B model handles well.
- A deterministic compiler writes Three.js β
compiler.pytranslates the scene graph into guaranteed-valid code, applies version-pinned Three.js r160, handles lighting, bloom, OrbitControls, and async font loading.
The model never touches the Three.js API. The preview never breaks on a bad token.
prompt β Qwen2.5-Coder-3B β JSON scene graph β validate/repair β compiler β Three.js HTML
Features
- Primitives β box, sphere, cylinder, cone, torus, torus knot, icosahedron, plane, ring, capsule, and more
- Extruded shapes β star, heart, arrow, hexagon, shield, custom SVG paths
- 3D text β FontLoader + TextGeometry, composited into extruded badge templates
- Deterministic templates β star badge, shield badge: the model picks shape + colors, Python handles layout
- Scene-graph groups β hierarchical
THREE.Group()with row / column / stack / grid layout, nested groups supported - Materials β glass, metal, neon glow, chrome, matte, wireframe, with roughness/metalness control
- Live preview β iframe with OrbitControls (drag to orbit, scroll to zoom)
- Copy-paste code β the Code tab is the complete HTML file; save it, double-click, done
- Lighting + glow controls β ambient, directional, environment map, UnrealBloomPass strength slider
Built small
Runs on Qwen2.5-Coder-3B-Instruct β a 3-billion-parameter model β on a single ZeroGPU T4. The model's job is intentionally narrow: emit a small JSON schema, not write a graphics library. Reliability comes from the compiler, not from model size.
Demo
Demo video: https://youtu.be/1NUB0DOGgcA
Social post: https://x.com/i/status/2066608249064030439
How it works (technical)
Pipeline:
llm.pyβ loads the model (or returns a mock for local dev), assembles a system prompt with the scene schema and ~30 few-shot examples, runs greedy generation, extracts the first valid JSON block from the outputscene.pyβ Pydantic v2 models for every node type (Obj,ExtrudeNode,Text3DNode,GroupNode,Animation); validators clamp out-of-range values and repair missing fields;_parse_scene_itemdispatches on"type"compiler.pyβ walks the scene graph with a_Ctxcontext object, emits Three.js JS for each node type, collects async font jobs forText3DNode; wraps everything in anasync IIFEinside a single self-contained HTML file with an importmap pinned to Three.js r160app.pyβ Gradio 6 UI with ZeroGPU@spaces.GPUdecorator;generate()builds the scene and compiles HTML;rerender()recompiles from cached scene JSON with new style/glow values without re-running the model
Three.js output uses a version-pinned importmap (r160) so the generated code remains valid regardless of CDN updates. All add-ons (OrbitControls, EffectComposer, UnrealBloomPass, RoundedBoxGeometry, FontLoader) are loaded from the same pinned release.
Future work
- Bloc3D mesh extension β extend the pipeline to emit geometry for actual mesh generation (the project's longer-term goal)
- Fine-tuning β synthetic promptβJSON pairs on a 0.5B model to push reliability further down in parameter count
- More templates β coin, ribbon, seal; parameterized layout macros
Files
| file | role |
|---|---|
scene.py |
scene DSL schema, Pydantic validation, node types |
llm.py |
model load, few-shot prompt, JSON extraction, mock fallback |
compiler.py |
scene graph β standalone Three.js HTML |
app.py |
Gradio UI, ZeroGPU wiring, style/glow controls |
GitHub
https://github.com/bolajiev/ThreeGen
Local dev
pip install -r requirements.txt
# No model download, no GPU β instant iteration:
MOCK=1 python app.py
# Real model:
python app.py