WVY-3D-23m
A tiny language model that writes 3D art as code.
WVY, designed by Starpower.
WVY-3D is a 23.1-million-parameter transformer trained from scratch. Give it a short prompt like "a pixel car" and pick a format. It writes the code that draws it:
- three.js: JavaScript that builds and returns a
THREE.Group - SVG: a complete standalone vector image
- SDF shader: a GLSL ES 3.0 fragment shader that raymarches the object as signed distance fields
23.1M parameters · 92 MB safetensors · ~275 tokens/s on CPU · three output formats
WVY-3D is the successor to WVY-Blender-5m. That model made selections: it picked tokens from a fixed scene language, and a procedural builder supplied the geometry. WVY-3D writes the geometry itself, as code, and learns to compose a style transformation with an object it has never seen in that style.
See it in motion
Every clip in the video is the model's first answer (seed 0, temperature 0.45). The code streams at the speed it was actually generated on an Apple M4 CPU, and the demo runs it exactly as written. The only change is a turntable spin on three.js objects. The prompts, seeds and the exact code for every clip are in demo_manifest.json.
The test: pairs it has never seen
The training data covers 25 objects × 6 styles × 3 formats. Five object + style pairs were held out completely: they never appear in training, in any format.
| Held-out pair | What the model has seen |
|---|---|
| pixel car | cars in 5 other styles; pixel art on 24 other objects |
| wireframe frog | frogs in 5 other styles; wireframes of 24 other objects |
| neon house | houses in 5 other styles; neon on 24 other objects |
| clay rocket | rockets in 5 other styles; clay on 24 other objects |
| low-poly bus | buses in 5 other styles; low poly on 24 other objects |
To draw a pixel car, the model has to apply the pixel transformation, learned on other objects, to the parts of a car it learned in other styles. Here is every held-out pair in every format, uncurated: seed 0, first try.
Two of the 15 fail an automated check, and they are marked. The pixel-car SVG passes the checks but only draws the wheels, a stripe and a taillight, which shows the checks don't catch everything.
Gallery
These are trained pairs across the vocabulary, one prompt each. Seed 0, first try, nothing re-rolled; we chose the prompts.
Every image is also in media/gallery/ at 640×640. The prompt, check results and full code for each one are in gallery_manifest.json.
Benchmark
All numbers come from the same checkpoint. A sample passes only if it does all of these:
- Runs: three.js executes in Node, the SDF compiles and links in WebGL2, and the SVG parses.
- Uses the style: it contains the style's signature idiom (voxel grid,
flatShading, glow, edges…). - Draws the object: its parts overlap the object's reference part list (Jaccard ≥ 0.6).
- Matches the palette: it uses the style's colours.
- Has the right plan line: its first-line comment names the object and style.
| Suite | Result |
|---|---|
| Held-out pairs, all formats (temp 0.45, 16 samples per pair × format) | 218 / 240 pass (91%) |
| · three.js | 74 / 80 |
| · SVG | 64 / 80 |
| · SDF shader | 80 / 80 |
| Trained sibling pairs (share an object or a style with a held-out pair) | 216 / 216 pass (100%) |
| Held-out pairs with phrasings never used in training ("show me a frog done in wireframe") | 54 / 75 |
| SDF shaders that compile and link across every suite | 326 / 327 |
Temperature. Held-out pass rates by sampling temperature:
| Temperature | Pass rate | Distinct outputs |
|---|---|---|
| 0 (greedy) | 85 / 90 | 38% |
| 0.3 | 81 / 90 | 96% |
| 0.45 (default) | 218 / 240 | 99% |
| 0.7 | 82 / 90 | 100% |
| 1.0 | 71 / 90 | 100% |
Loss. Teacher-forced loss on gold code is 0.055 nats/token for trained pairs and 0.141 for held-out pairs (perplexity 1.06 vs 1.15). The gap is largest for SVG and smallest for SDF.
Speed. On an Apple M4 CPU (4 threads, batch 1), decoding runs at about 275 tokens/s and the first token arrives in about 6 ms. A three.js object (200 tokens) takes under a second; an SDF shader (540 tokens) about 2 s. Peak memory is about 535 MB.
The full results, per pair and per check, are in benchmark.json.
Known limitations
- Fixed vocabulary. It knows 25 objects (car, truck, bus, boat, plane, rocket, frog, cat, duck, pig, fish, robot, snowman, mushroom, tree, pine, cactus, flower, house, bench, chair, table, lamp, fountain, castle) and 6 styles (smooth, pixel, low poly, clay, neon, wireframe). Other words are not reliably understood.
- Colour and size words slip in the plan line. With prompts like "a big red pixel car", the code usually applies the colour (70 / 90) and the size (45 / 45) correctly. But the first-line plan comment often names the wrong object (correct in 3 / 90). This is the main thing the next version fixes.
- Pixel cars in three.js sometimes stop before the voxel-mesh block (10 / 16 pass). This was the cause of every three.js runtime failure in the benchmark.
- SVG is the weakest format on held-out pairs. The low-poly bus SVG sometimes draws car parts (6 / 16 pass), and the neon house SVG sometimes uses off-palette colours (11 / 16).
- Neon in three.js is lit like everything else in the viewer's daylight scene, so it reads as bright colour rather than glow.
- The training data is procedural. All 200,000 training samples were generated by our own builders, so outputs share that visual language. It does not make arbitrary meshes.
What it was trained on
WVY-3D was trained from scratch on 200,000 prompt → code pairs that we generated locally. No scraped code, images or pretrained models were used.
Each sample is built from three factors: an object (a list of named parts with shapes, sizes and colours), a style (a transformation applied to those parts, such as voxelizing, faceting, glow or edges), and a format (the emitter that writes three.js, SVG or GLSL). The five held-out pairs are removed from training entirely. Prompts vary their wording ("8-bit", "voxel", "blocky" for pixel; "sedan", "auto" for car), and may add colour and size words.
Each sample starts with a plan line, e.g. // wvy-3d: car, pixel, so the model commits to what it is drawing before it writes code.
Model details
| Property | Value |
|---|---|
| Published name | WVY-3D-23m |
| Author | Starpower |
| Parameters | 23,120,640 |
| Architecture | Decoder-only causal transformer (GPT-style), pre-LayerNorm, GELU MLP, tied input/output embeddings |
| Layers | 12 |
| Hidden size | 384 |
| Attention heads | 6 |
| Feed-forward width | 1,536 |
| Context window | 768 tokens |
| Vocabulary | 4,096 (BPE learned on the corpus; numbers and hex colours kept whole as single tokens) |
| Training | 3,000 steps, batch 16 × 768 tokens, AdamW (β 0.9/0.95, weight decay 0.1), lr 6e-4 with warmup and cosine decay, Apple-silicon GPU |
| Final validation loss | 0.072 |
| Default decoding | temperature 0.45, top-k 40, top-p 0.95, KV cache |
| Weight format | safetensors, FP32 |
| Pretrained base model | None; trained from scratch |
Run it locally
pip install torch safetensors huggingface_hub
hf download StarpowerTechnology/WVY-3D-23m --local-dir WVY-3D-23m
cd WVY-3D-23m
python generate.py "a pixel car" # three.js
python generate.py "a neon house" --format sdf # GLSL fragment shader
python generate.py "a clay rocket" --format svg --out rocket.svg
From Python:
import wvy3d
model, tok = wvy3d.load("StarpowerTechnology/WVY-3D-23m") # or a local folder
code = wvy3d.generate(model, tok, "a wireframe frog", "threejs", seed=0)
Running what it writes
- three.js: the code is a function body that receives
THREEand returns the object.const obj = new Function('THREE', code)(THREE); scene.add(obj); - SVG: save it as a
.svgfile and open it. It's a complete document. - SDF: a GLSL ES 3.0 fragment shader. Draw a full-screen triangle and set
uniform vec2 R(resolution in pixels) anduniform float T(time in seconds). It writes toout vec4 O.
The Space does all of this in the browser, and can also export three.js output as GLB.
Files
| File | What it is |
|---|---|
model.safetensors |
Weights (the output head is tied to the token embedding) |
config.json |
Architecture, plus the object/style/format vocabulary and the held-out pairs |
tokenizer.json |
BPE tokenizer |
wvy3d/ |
Model, tokenizer and loader (about 370 lines of PyTorch) |
generate.py |
Command-line generator |
benchmark.json |
Full benchmark results |
demo_manifest.json |
Prompt, seed and exact code for every clip in the video |
gallery_manifest.json |
Prompt, checks and exact code for every gallery image |
media/ |
Video, cover, gallery sheets and individual renders |
License
Apache-2.0.
- Downloads last month
- -


