Spark-X2.6-4B-Blender

A 4B model that writes Blender 5.x Python (bpy) scripts which build clean, quad-dominant meshes: hard-surface props, furniture, products, and anatomically proportioned organic base meshes (humans, hands, quadrupeds).

Full-parameter fine-tune of XHToken/Spark-X2.6-4B-Base (all 4.1B weights, no adapters). Every training script was executed headless in Blender and kept only if it ran and produced geometry; topology was scored on the evaluated mesh.

Results

All numbers come from running the model's output in Blender 5.2.1 LTS headless, greedy decoding. run = script executed and built geometry · topo = mean topology score of those (max 1.0) · fid = fidelity to the reference answer (size, proportions, part count; failed scripts count as 0).

Model Stage BlendNet held-out Anatomy (unseen) 3DCode held-out Spatial (held-out) BlenderWorld text Extreme (unseen) Hard constraints Objaverse (held-out) Real-world prompts
base untuned XHToken/Spark-X2.6-4B-Base 2.0% run · topo 0.955 · fid 0.004 0.0% run · topo 0.000 · fid 0.000 2.0% run · topo 1.000 · fid 0.005 6.7% run · topo 0.750 · fid 0.049 · QA 0/100 0.0% run · topo 0.000 – – – –
v1 full fine-tune (SFT) on verified BlendNet + procedural anatomy 99.0% run · topo 0.877 · fid 0.811 100.0% run · topo 0.994 · fid 0.959 42.0% run · topo 0.943 · fid 0.161 95.0% run · topo 1.000 · fid 0.803 · QA 0/100 – – – – –
v3 v1 + rejection-sampling round (best-of-N winners plus agent repair / refine / polish turns) 100.0% run · topo 0.943 · fid 0.801 100.0% run · topo 0.994 · fid 0.963 40.0% run · topo 0.976 · fid 0.162 100.0% run · topo 1.000 · fid 0.979 · QA 35/100 66.7% run · topo 0.940 exact 0.0% · score 0.043 · free 0.0% constraint 0.0% · score 0.077 norm 0.238 · beats box 49/78 · judge top-20 10% (median rank 206) 100.0% run · topo 0.958
v4 v3 + short submit warm-up SFT + agentic GRPO-family RL (multi-turn episodes in Blender, DAPO loss, verifier reward) – – – – – – – – –
v5 v3 + SFT on Blender-verified procedural generator data (lathe/panel: curved, bevelled, materials) + anatomy/spatial/BlendNet replay 99.0% run · topo 0.936 · fid 0.792 100.0% run · topo 0.995 · fid 0.974 30.0% run · topo 0.975 · fid 0.128 100.0% run · topo 1.000 · fid 0.997 · QA 38/100 53.8% run · topo 0.963 exact 0.0% · score 0.093 · free 0.0% constraint 0.0% · score 0.071 – · judge top-20 12% (median rank 263) 87.5% run · topo 0.955
v6 v5 + full SFT on part-composition data (numbers-as-variables, unseen kits) + replay of generator/anatomy/agent/BlendNet/spatial/general rows 99.0% run · topo 0.946 · fid 0.785 100.0% run · topo 0.995 · fid 0.983 32.0% run · topo 0.962 · fid 0.164 100.0% run · topo 1.000 · fid 0.998 · QA 41/100 46.2% run · topo 0.939 exact 0.0% · score 0.205 · free 0.0% constraint 0.0% · score 0.074 – · judge top-20 13% (median rank 351) –
v7 full SFT from base on one deduplicated pool (BlendNet whole, generator/fit/anatomy capped per template) 100.0% run · topo 0.893 · fid 0.797 100.0% run · topo 0.997 · fid 0.951 36.0% run · topo 0.949 · fid 0.146 100.0% run · topo 1.000 · fid 0.997 · QA 40/100 30.8% run · topo 0.956 exact 0.0% · score 0.091 · free 0.0% constraint 0.0% · score 0.069 – · judge top-20 6% (median rank 492) 87.5% run · topo 0.978
v3d v3 + DPO on its own best-of-N samples (best finished answer vs loop/failure) 100.0% run · topo 0.942 · fid 0.802 100.0% run · topo 0.994 · fid 0.963 42.0% run · topo 0.971 · fid 0.169 100.0% run · topo 1.000 · fid 0.979 · QA 35/100 76.9% run · topo 0.946 exact 0.0% · score 0.046 · free 0.0% constraint 0.0% · score 0.081 norm 0.235 · beats box 51/78 · judge top-20 12% (median rank 208) 100.0% run · topo 0.960
v3dr v3d + DPO on open-world samples graded against real meshes (Objaverse-LVIS training categories) 98.0% run · topo 0.961 · fid 0.788 100.0% run · topo 0.997 · fid 0.964 42.0% run · topo 0.918 · fid 0.173 100.0% run · topo 1.000 · fid 0.981 · QA 34/100 79.5% run · topo 0.949 exact 0.0% · score 0.036 · free 0.0% constraint 0.0% · score 0.077 norm 0.466 · beats box 58/78 · judge top-20 11% (median rank 214) 100.0% run · topo 0.959
v3drr v3dr + DPO round 2 on fresh open-world samples (same real-mesh grading) 99.0% run · topo 0.953 · fid 0.791 100.0% run · topo 0.998 · fid 0.965 40.0% run · topo 0.934 · fid 0.165 98.3% run · topo 1.000 · fid 0.964 · QA 34/100 79.5% run · topo 0.956 exact 0.0% · score 0.029 · free 0.0% constraint 0.0% · score 0.067 norm 0.589 · beats box 58/78 · judge top-20 17% (median rank 226) 100.0% run · topo 0.967
v3drrr v3drr + DPO round 3 with fp32 master weights (fp32 master copy + bf16 Adam moments) 100.0% run · topo 0.962 · fid 0.797 100.0% run · topo 0.998 · fid 0.964 30.0% run · topo 0.964 · fid 0.123 98.3% run · topo 1.000 · fid 0.962 · QA 33/100 76.9% run · topo 0.949 exact 0.0% · score 0.039 · free 0.0% constraint 0.0% · score 0.074 norm 0.634 · beats box 59/78 · judge top-20 16% (median rank 245) 100.0% run · topo 0.969
v3w1 v3d + SFT on part-family kit generators for ~180 open-world object families (detailed and vague prompts) + replay – – – – – – – – –

Held-out BlendNet rows and anatomy eval prompts are never seen in training (anatomy prompts use a different random seed). BlenderWorld is scored for execution and topology only; its rubric judge was not run.

Every stage is also compared with the base model on identical prompts: exact McNemar test on which prompts pass, shape F-score@0.05 and Chamfer distance against the reference's surface point cloud (2048 area-weighted samples, both normalised for placement and size), with 95% bootstrap intervals. A stage counts as better only if it wins on every suite at p < 0.05 without general-text perplexity rising more than 1.05× (criteria fixed before results).

Sample outputs: best and worst

Rendered straight from this model's generated scripts on held-out prompts (greedy, one attempt), ranked by the suite's metric.

BlendNet: 100/100 scripts built (failures render nothing).

fidelity prompt output
best 1.000 Design a pet feeding bowl with a single compartment.
best 1.000 Create a 3D model of a sandwich. The sandwich should have two slices of bread with a filling in between. The filling should consist of layers of ingredients …
worst 0.219 Design a 3D model of a company logo. The logo should be simple and geometric, featuring a combination of circles and lines.
worst 0.393 Can you design a water bottle with a cylindrical body and a screw-on cap? Make sure it includes a loop for easy carrying. Add a measurement scale on the side.

Anatomy: 60/60 scripts built (failures render nothing).

fidelity prompt output
best 0.998 Make a low-poly-to-subdivided base mesh of a athletic female human body, 1.58 m tall (7.2 heads), in A-pose with anatomically correct proportions.
best 0.994 I need a cat body (0.39 m from shoulder to rump), standing, digitigrade legs and a tail for sculpting later. Keep the topology clean and quad-based.
worst 0.920 I need a average male human body, 1.58 m tall (7.5 heads), in relaxed for sculpting later. Keep the topology clean and quad-based.
worst 0.920 I need a athletic male human body, 1.90 m tall (7.5 heads), in relaxed for sculpting later. Keep the topology clean and quad-based.

3DCode: 20/50 scripts built (failures render nothing).

fscore prompt output
best 1.000 Create the 3D object of a double panel door using Python Blender code. Start by defining the main door slab using a scaled cube primitive with a height of …
best 0.996 Create the 3D object of an arched panel door using Python Blender code. Begin by constructing the main rectangular door slab from a scaled cube primitive. Form …
worst 0.000 Create the 3D object of a leather coral using Python Blender code. Start by generating a base polygon mesh consisting of two distinct colony centers using a …
worst 0.094 Create the 3D object of a pair of hybrid chopsticks using Python Blender code. Construct each chopstick with two main components: a thick wooden handle and a …

Spatial assemblies: 60/60 scripts built (failures render nothing).

fidelity prompt output
best 1.000 Create a table with a 0.9612 m by 1.0932 m top 0.0428 m thick, 0.9819 m tall overall, on four square legs 0.0648 m wide, each inset 0.056 m from the top's …
best 1.000 Model a shelf unit 0.8004 m wide, 0.2748 m deep and 1.6027 m tall: two side panels and 4 evenly spaced boards (bottom on the floor, top flush with the sides), …
worst 0.756 Create a grid of 5 rows by 4 columns of 0.1164 m cubes with 0.1154 m gaps between them, centred on the origin and resting on the ground. Compute each part's …
worst 0.862 Build a grid of 3 rows by 3 columns of 0.2442 m cubes with 0.337 m gaps between them, centred on the origin and resting on the ground. Place every part …

Language-model metrics (same tokens, run vs base)

Run Text Perplexity base → run Top-1 token acc. KL(run‖base) per token Loss change [95% CI]
v1 domain (all) 1.49 → 1.08 0.891 → 0.973 0.329 -0.325 [-0.346, -0.307]
v1 domain (clean) 1.49 → 1.09 0.892 → 0.969 0.318 -0.311 [-0.331, -0.292]
v1 general (wikitext-2) 9.73 → 11.43 0.519 → 0.499 0.162 +0.161 [+0.149, +0.173]
v3 domain (all) 1.49 → 1.09 0.891 → 0.970 0.334 -0.317 [-0.337, -0.299]
v3 domain (clean) 1.49 → 1.10 0.892 → 0.966 0.323 -0.302 [-0.322, -0.283]
v3 general (wikitext-2) 9.73 → 10.44 0.519 → 0.507 0.093 +0.070 [+0.063, +0.076]
v5 domain (all) 1.49 → 1.09 0.891 → 0.970 0.336 -0.312 [-0.332, -0.295]
v5 domain (clean) 1.49 → 1.10 0.892 → 0.966 0.325 -0.298 [-0.317, -0.278]
v5 general (wikitext-2) 9.73 → 10.53 0.519 → 0.505 0.103 +0.079 [+0.073, +0.085]
v6 domain (all) 1.49 → 1.10 0.891 → 0.969 0.342 -0.308 [-0.328, -0.290]
v6 domain (clean) 1.49 → 1.11 0.892 → 0.966 0.331 -0.294 [-0.314, -0.274]
v6 general (wikitext-2) 9.73 → 10.50 0.519 → 0.506 0.103 +0.076 [+0.069, +0.083]
v7 domain (all) 1.49 → 1.08 0.891 → 0.973 0.324 -0.325 [-0.344, -0.307]
v7 domain (clean) 1.49 → 1.09 0.892 → 0.969 0.314 -0.311 [-0.330, -0.292]
v7 general (wikitext-2) 9.73 → 10.25 0.519 → 0.511 0.077 +0.052 [+0.046, +0.058]
v3d domain (all) 1.49 → 1.09 0.891 → 0.970 0.335 -0.317 [-0.337, -0.299]
v3d domain (clean) 1.49 → 1.10 0.892 → 0.966 0.324 -0.302 [-0.322, -0.283]
v3d general (wikitext-2) 9.73 → 10.44 0.519 → 0.506 0.093 +0.070 [+0.063, +0.076]
v3dr domain (all) 1.49 → 1.09 0.891 → 0.970 0.340 -0.315 [-0.335, -0.297]
v3dr domain (clean) 1.49 → 1.10 0.892 → 0.966 0.329 -0.300 [-0.320, -0.281]
v3dr general (wikitext-2) 9.73 → 10.43 0.519 → 0.507 0.093 +0.069 [+0.063, +0.076]
v3drr domain (all) 1.49 → 1.10 0.891 → 0.969 0.345 -0.310 [-0.330, -0.292]
v3drr domain (clean) 1.49 → 1.11 0.892 → 0.965 0.334 -0.295 [-0.314, -0.275]
v3drr general (wikitext-2) 9.73 → 10.43 0.519 → 0.507 0.093 +0.069 [+0.063, +0.076]
v3drrr domain (all) 1.49 → 1.10 0.891 → 0.968 0.348 -0.306 [-0.326, -0.288]
v3drrr domain (clean) 1.49 → 1.11 0.892 → 0.965 0.338 -0.291 [-0.311, -0.271]
v3drrr general (wikitext-2) 9.73 → 10.43 0.519 → 0.507 0.093 +0.070 [+0.063, +0.076]

Checkpoints

main holds the best stage so far (by held-out evals) — currently the v3 weights above; mid-training checkpoints are not published to this public repo. Finished runs are backed up to the private repo hotocooo/Spark-X2.6-4B-Blender-checkpoints (v1/, warm_v4/, v5/, v3/, v6/, v6g/, v7/, v7a/, v7d/, v3d/, v3dr/, v3drr/): a local copy is deleted only after every uploaded file's sha256 matches the remote LFS object (small files: checked by size), and GGUFs are rebuilt on demand from the restored weights.

Quick start

from transformers import AutoModelForCausalLM, AutoTokenizer

repo = "hotocooo/Spark-X2.6-4B-Blender"
tok = AutoTokenizer.from_pretrained(repo, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(repo, trust_remote_code=True, torch_dtype="bfloat16", device_map="auto")

prompt = '### Task\nWrite a Blender 5 Python (bpy) script that builds this, with clean quad topology:\nA wooden stool with three legs and a round seat.\n\n### Script\n'
ids = tok(prompt, return_tensors="pt").to(model.device)
out = model.generate(**ids, max_new_tokens=4096, do_sample=False, top_k=0)
script = tok.decode(out[0, ids["input_ids"].shape[1]:], skip_special_tokens=True)
print(script)

Run the script in Blender: blender -b --python script.py or paste it into the Scripting workspace.

Requirements: transformers==4.57.1 (the architecture code ships with the repo and targets the 4.x API), trust_remote_code=True. It is a completion model, not a chat model: use the prompt format below.

Prompt format

### Task
Write a Blender 5 Python (bpy) script that builds this, with clean quad topology:
<describe what to build>

### Script

Stop generation at the end-of-sequence token or at ###.

Anatomy base meshes

The anatomy data is procedural: anatomically proportioned joint skeletons (human: 7.5/7.2-head canons, male/female, slim/average/athletic/heavy builds, T-pose, A-pose, relaxed; hands: three phalanges per finger, opposable thumb, curl; quadrupeds: dog, cat, horse, deer, bear, digitigrade / unguligrade / plantigrade legs) wrapped by Blender's Skin modifier and smoothed with Subdivision Surface, the standard way to block in a sculpt-ready base mesh. Left/right symmetry is exact (mirrored in the data, not with a Mirror modifier, which would weld the centre line).

Rendered anatomy training examples

Examples above (front and side, wireframe shows edge flow):

  • Make a low-poly-to-subdivided base mesh of a heavy female human body, 1.62 m tall (7.2 heads), in T-pose with anatomically correct proportions.
  • Create a human left hand, 19 cm long, fingers flat and open, with three phalanges per finger. Use clean all-quad topology suitable for sculpting.
  • Model a deer body (1.40 m from shoulder to rump), standing, unguligrade legs and a tail as a base mesh with good edge flow and no n-gons.

Training

Data

Source Rows Notes
BlendNet (raw) 12,059 FreedomIntelligence/BlendNet @ 11a779c
BlendNet passing Blender check 11,653 ran headless in Blender, produced geometry
BlendNet train / held-out 11,353 / 300 held-out never trained on; evals use the first 100
Procedural anatomy 2,400 human 1200, hand 400, quadruped 800; topology ≥ 0.9
3DCode procedural assets (train / held-out) 2,266 / 977 YipengGao/3DCode @ 0db485b, Infinigen-derived; ≤ 6144 tokens, ran in Blender; 20 whole object factories held out
Spatial / geometry / maths (train / held-out) 2,800 / 160 procedural: part assemblies placed by computed positions (Blender-checked size + part count) and worked QA on transforms, vertex / face counts, bounding boxes, vector geometry (Blender or closed-form checked)
Best-of-8 winners (bon_1) 950 self-generated, beat reference topology, fidelity ≥ 0.6

Method

Multi-stage, every stage full-parameter and verified in Blender:

  1. SFT (v1): full fine-tune on verified BlendNet + procedural anatomy, loss on script tokens only. (A long-context SFT stage on Infinigen 3DCode scripts was tried and dropped: it memorised the training factories and fell from 21/50 to 5/50 on held-out ones.)
  2. Spatial grounding (v3 onward): verified procedural data so placement is computed, not guessed (a script can run and still put every part in the wrong place): part assemblies, local-to-world transforms in Blender's scale / rotate / translate order, vertex / face counts through modifiers, bounding boxes, vector geometry. Trained in SFT and rewarded in RL (assemblies by shape match, QA by the verified answer).
  3. Rejection sampling (v3): sample 8 scripts per prompt, run all in Blender, keep the best only if it beats the reference's topology and matches the reference (stops "a lone cube" from winning).
  4. Reinforcement learning (v4): multi-turn GRPO-family policy optimisation (DAPO token-level loss, no KL term, truncated outputs masked) with the Blender verifier as the reward. Each prompt is an agent episode: the model writes a script, sees Blender's verdict (the error, or a scene report: parts, size, quad ratio, topology), then writes a better script or submits; the whole episode is one trajectory with Blender's text masked out. v3 is taught the loop first with best-of-N repair, refine and polish examples; a short SFT on the same rows plus restored submit examples (v3 itself saw none: every submit target became a realism polish) teaches stopping before RL. Side task adapted from CodeRL+: read a script, predict its object count and size, rewarded against what Blender actually builds.
  5. Selection: after each stage's held-out evals, main is replaced only if that stage beats every earlier one (currently v3); only the best model is kept.

No outputs of proprietary models (e.g. Claude, GPT, Gemini) are used as training data.

Topology score

On the evaluated mesh (modifiers applied), summed over objects: score = 1.0·quad_ratio − 0.5·ngon_ratio − 1.0·non_manifold_ratio − 0.3·pole_ratio − 1.0·degenerate_ratio − 1.0·loose_ratio, each term a ratio (quads/faces, n-gons/faces, non-manifold edges/edges, bad poles/verts, degenerate faces/faces, loose verts/verts). Bad poles are interior vertices with valence outside 3–5; 3- and 5-poles are normal in quad meshes and not penalised.

Hyperparameters

Fine-tune type full parameter (all weights), no adapters
Precision bf16 weights, grads and optimizer state
Optimizer AdamW (torch-optimi, Kahan summation), betas (0.9, 0.99), wd 0.01
SFT learning rate 1e-05, cosine, warmup 0.03
SFT batch v1 2 × 8; later stages 1 × 16 (chunked loss); effective 16
SFT epochs 1; max sequence v1 2048, later stages 6144 tokens; loss on script tokens only
Rejection round 600 prompts × 8 samples, T=0.8, top-p 0.95; replay 2000 rows
RL DAPO loss, group 8, lr 1e-06, on-policy (one update per rollout), KL β 0.0, 200 prompts + 25% execution-prediction prompts, truncated completions masked
Agent episodes up to 3 scripts: after each, Blender's error or scene report; the model refines or answers SUBMIT (observations masked out of the RL loss, reward × 0.9 per extra script)
RL reward 0.15·runs + 0.25·topology + 0.2·fidelity + 0.25·shape + 0.15·realism (0 if script fails)
Attention fused SDPA (verified identical loss and greedy tokens vs. the reference eager path)
Hardware Apple M4 Max, 64 GB unified memory, PyTorch MPS

Reproducibility

Pinned: base model XHToken/Spark-X2.6-4B-Base @ e1bd659ae2b1c2263b2fc2544485976c1b516115, dataset FreedomIntelligence/BlendNet @ 11a779c7723af9bacbed371fccdc643ea20be446, transformers==4.57.1. Pipeline: Blender-verified data filter, procedural anatomy generator, full fine-tune, best-of-N, GRPO, eval, export; each stage resumes from its own checkpoints. Eval generation: base model and v1 through transformers (bf16, MPS); later stages through llama.cpp llama-server on an f16 GGUF of the same weights (identical token ids, same sampler settings), 6.5x faster. Same prompts and Blender scoring throughout.

Limitations

  • Base-mesh quality, not sculpts. Anatomy is blockout level (correct proportions and clean quads, ready to sculpt); muscle definition, faces and fine detail are beyond what a 4B script-writing model produces.
  • Script, not mesh. The model writes code; geometry exists only after Blender runs it. Some scripts still fail, see the run rate above.
  • Topology metric is structural. It rewards quads, manifold geometry and clean poles; it does not judge aesthetics. Fidelity checks size, proportions and part count against a reference, not exact shape.
  • Open-world objects are weak. Outside the trained families (BlendNet-style props, furniture and products from the generator kits, anatomy), a vague prompt such as "Make a banjo." mostly yields a stack of primitives with the right part names in the wrong places. The Objaverse column's F-score norm does not show this: it rates a two-box "bed" above a box. The judge number beside it does: a clay render is ranked against real-object renders of all ~1,124 Objaverse-LVIS categories by a SigLIP image encoder, and the true category must land in the top 20. Kit-built objects reach rank 1-20; this model's held-out outputs sit at a median rank near 200.
  • English prompts, Blender 5.x API.

Safety

Generated scripts are executable Python. Run them in an isolated Blender, not one holding unsaved work. Training and evaluation statically rejected scripts importing anything beyond bmesh, bpy, collections, colorsys, dataclasses, functools, hashlib, itertools, math, mathutils, numpy, random, statistics, types, typing, warnings or touching files, network, add-ons or the app (DataSource, addon_enable, addon_install, export_scene, filepath, fromfile, genfromtxt, import_scene, libraries, load, loadtxt, memmap, open_mainfile, python_file_run, quit_blender, read_homefile, save, save_as_mainfile, save_mainfile, savetxt, savez, savez_compressed, tofile, url_open); getattr/setattr/delattr/hasattr were replaced by guarded versions that refuse those names at run time. These filters are not a sandbox.

Special thanks

Special thanks to ACOTech for providing the machine behind this. Every step ran on it, locally: data verification, every fine-tune stage, the held-out evaluations and every benchmark on this page. The machine is a Mac Studio with an Apple M4 Max, 64 GB of unified memory.

How this was made

The data pipeline, fine-tunes, evaluation, benchmarks and this page were done with Claude Code running Claude Opus 5.5, directed and reviewed by hotocooo.

Citation

@misc{spark_x2_6_4b_blender,
  title  = {Spark-X2.6-4B-Blender: full fine-tune of Spark-X2.6-4B for Blender bpy modeling},
  author = {hotocooo},
  year   = {2026},
  url    = {https://huggingface.co/hotocooo/Spark-X2.6-4B-Blender}
}
@misc{sparkx2.6,
  title  = {Spark-X2.6-4B: Advancing the Frontier of On-Device Agents for Real-World Tasks},
  author = {SparkLLM Team},
  year   = {2026}
}
@misc{du2024blenderllmtraininglargelanguage,
  title  = {BlenderLLM: Training Large Language Models for Computer-Aided Design with Self-improvement},
  author = {Yuhao Du and Shunian Chen and Wenbo Zan and Peizhao Li and Mingxuan Wang and Dingjie Song and Bo Li and Yan Hu and Benyou Wang},
  year   = {2024},
  eprint = {2412.14203},
  archivePrefix = {arXiv}
}

License: Apache-2.0, same as the base model and BlendNet. 3DCode is MIT; cite 3DCodeBench and Infinigen when using it. General-text replay uses FineWeb-Edu (ODC-By 1.0) by Hugging Face.

Downloads last month
712
Safetensors
Model size
4B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for hotocooo/Spark-X2.6-4B-Blender

Finetuned
(4)
this model
Quantizations
1 model

Datasets used to train hotocooo/Spark-X2.6-4B-Blender

Paper for hotocooo/Spark-X2.6-4B-Blender