import gradio as gr import spaces # ponytail: never called; ZeroGPU hardware refuses to boot without one @spaces.GPU function. Delete if the Space moves # to cpu-basic (needs PRO to downgrade). @spaces.GPU def _zerogpu_stub(): pass SYSTEM_PROMPT = ( "Use only the Three.js core ES module from\n" "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.0/three.module.min.js.\n" "Do not import addons or legacy examples/js paths;\n" "implement any camera controls directly inside the HTML." ) EXAMPLES = [ "Please create one self-contained HTML file with Three.js from cdnjs showing an elephant rehabilitation habitat.\n\n" "A broad sheltered yard connects water, shade, and feeding areas through accessible paths. Use the elephant's scale " "and movement clearance to guide the layout around the structures.\n\n" "Render it as a deliberate 3D voxel model built from visible cubic blocks, with clear depth, readable silhouettes, " "and solid spatial relationships.", "Build a self-contained HTML scene with Three.js from cdnjs featuring a dragonrider aerie on a sea stack, with " "perches, a hatchery, and a windlass.\n\nThe aerie occupies a flat-topped sea stack, reached by a rope windlass " "from the shore. Four projecting stone perches ring the central hatchery, a vaulted cave with sand nests and a " "smoke hole. A timber landing deck holds a hoist crane and harness racks, while switchback stairs connect the " "perches to a water-level boathouse.\n\nRender it as a deliberate 3D voxel model built from visible cubic blocks, " "with clear depth, readable silhouettes, and solid spatial relationships.", "Please create one self-contained HTML file with Three.js from cdnjs showing a canal lock and weir complex with a " "central island.", "Make a standalone HTML scene with Three.js from cdnjs depicting an animated air traffic control tower with rotating " "radar and light signals.\n\nA tapered control tower rises from a rectangular base to a cantilevered glass cab with " "angled glazing panels and an external catwalk. A primary surveillance radar on a roof pedestal turns continuously, " "and a rotating beacon sits above the cab.", ] EXAMPLE_LABELS = [ "Elephant rehabilitation habitat", "Dragonrider aerie on a sea stack", "Canal lock and weir complex", "Animated air traffic control tower", ] # prefill the code fence so the model skips chatty preambles ("I cannot create files...") NO_THINK = "\n\n\n\n```html\n" # window.wgLive(rawOutput) re-renders the partial scene into #wg-live every few seconds. # Unfinished JS is cut back to the last complete-looking line, open brackets are closed and a fallback render loop # is appended; the new iframe only replaces the old one once that patched script actually ran (it posts 'wg-ok'). LIVE_JS = r""" window.wgCode ??= (out) => { const body = out.replace(/[\s\S]*?(<\/think>|$)/g, ''); let m = body.match(/```html\s*([\s\S]*?)(```|$)/); return m ? m[1].trim() : ((m = body.match(/|$)/i)) ? m[0] : body.trim()); }; window.wgLive ??= (() => { const TAIL = "\n;try{const R=renderer,S=scene,C=camera;(function f(){try{R.render(S,C)}catch(e){}" + "requestAnimationFrame(f)})()}catch(e){}\nparent.postMessage('wg-ok','*');\n"; const closers = (s) => { const st = []; for (let i = 0; i < s.length; i++) { const c = s[i]; if (c === '/' && s[i + 1] === '/') { i = s.indexOf('\n', i); if (i < 0) break; } else if (c === '/' && s[i + 1] === '*') { i = s.indexOf('*/', i + 2) + 1; if (i <= 0) break; } else if (c === '"' || c === "'" || c === '`') { while (++i < s.length && s[i] !== c) if (s[i] === '\\') i++; } else if ('([{'.includes(c)) st.push({ '(': ')', '[': ']', '{': '}' }[c]); else if (')]}'.includes(c)) st.pop(); } return st.reverse().join(''); }; // -> [html, isPartial], or null when there is no script to run yet const patch = (code) => { const i = code.lastIndexOf('', i) + 1; if (i < 0 || !j) return null; if (/<\/script>/i.test(code.slice(j))) return [code, false]; const lines = code.slice(j).split('\n'); lines.pop(); while (lines.length && !/[;{},]\s*$/.test(lines.at(-1))) lines.pop(); const js = lines.join('\n'); return [code.slice(0, j) + js + '\n' + closers(js) + TAIL, true]; }; let latest = '', last = 0, timer = 0, pending = null, shown = ''; const box = () => document.querySelector('#wg-live'); const show = (f) => { if (pending !== f) return; pending = null; shown = f.srcdoc; for (const c of [...box().children]) if (c !== f) c.remove(); f.style.visibility = 'visible'; }; addEventListener('message', (e) => { if (pending && e.source === pending.contentWindow && e.data === 'wg-ok') show(pending); }); const render = () => { timer = 0; const p = patch(window.wgCode(latest)); if (!p || p[0] === shown || p[0] === pending?.srcdoc) return; if (pending && p[1]) { timer = setTimeout(render, 1000); return; } // let the in-flight partial finish first pending?.remove(); last = performance.now(); const f = pending = document.createElement('iframe'); f.sandbox = 'allow-scripts'; f.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;border:0;visibility:hidden'; if (!p[1]) f.onload = () => show(f); // final scene: show it even if it errors setTimeout(() => { if (pending === f) { f.remove(); pending = null; } }, 15000); f.srcdoc = p[0]; box().append(f); }; return (raw) => { latest = raw; if (!raw) { clearTimeout(timer); timer = 0; pending = null; shown = ''; box().replaceChildren(); return; } timer ||= setTimeout(render, Math.max(0, 5000 - (performance.now() - last))); }; })(); """ # llama.cpp runs in the browser via wllama. WEBGPU_JS = r""" async (prompt, thinking, maxTokens, temperature, topP, topK) => { __LIVE__ const st = document.querySelector('#wg-status'); const esc = (s) => s.replace(/&/g, '&').replace(/ ((b - a) / 1000).toFixed(1); const S = window.wgS = { phase: 'Loading wllama runtime', tail: '' }; let k = 0, out = '', n = 0, tLoad, tFirst, cpu = ''; // navigator.gpu can exist with no usable adapter (headless, blocklisted GPU); wllama then silently runs on CPU Promise.resolve(navigator.gpu?.requestAdapter()).then((a) => { if (!a) cpu = '
No WebGPU adapter, running on CPU (very slow). Use Chrome/Edge 113+ with a GPU.'; }); const tick = setInterval(() => { st.innerHTML = `${'⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'[k++ % 10]} ${S.phase} · ${sec(t0)}s elapsed${cpu}` + (S.tail && `
${esc(S.tail)}
`); const pre = st.querySelector('pre'); if (pre) pre.scrollTop = pre.scrollHeight; // follow the newest tokens }, 100); window.wgLive(''); try { window.wg ??= (async () => { const V = 'https://cdn.jsdelivr.net/npm/@wllama/wllama@3.8.1/esm/'; const { Wllama } = await import(V + 'index.js'); const w = new Wllama({ default: V + 'wasm/wllama.wasm' }); const load = () => w.loadModelFromHF({ repo: '__REPO__', quant: 'Q4_K_M' }, { n_ctx: 18432, n_gpu_layers: 999, progressCallback: ({ loaded, total }) => { window.wgS.phase = loaded < total ? `Downloading model (cached after first run): ${(loaded / 1e9).toFixed(2)} / ${(total / 1e9).toFixed(2)} GB` : 'Loading model onto GPU'; }, }); // an interrupted download leaves a partial cache that wllama won't repair; wipe it and retry once try { await load(); } catch (e) { window.wgS.phase = 'Model cache incomplete, re-downloading'; await w.cacheManager.clear(); await load(); } return w; })().catch((e) => { window.wg = null; throw e; }); const w = await window.wg; tLoad = performance.now(); S.phase = 'Reading prompt (prefill)'; const text = `<|im_start|>system\n__SYSTEM__<|im_end|>\n<|im_start|>user\n${prompt}<|im_end|>\n<|im_start|>assistant\n` + (thinking ? '\n' : '__NO_THINK__'); out = thinking ? '\n' : '```html\n'; const it = await w.createCompletion({ prompt: text, max_tokens: maxTokens, temperature, top_p: topP, top_k: topK, penalty_repeat: 1.0, stop: ['<|im_end|>'], stream: true }); for await (const c of it) { out += c.choices[0].text; tFirst ??= performance.now(); S.phase = `Generating in your browser: ${++n} tokens, ${(n * 1000 / (performance.now() - tFirst + 1)).toFixed(1)} tok/s`; S.tail = out.slice(-800); window.wgLive(out); } const tEnd = performance.now(); tFirst ??= tEnd; const msg = `Done in ${sec(t0, tEnd)}s: model load ${sec(t0, tLoad)}s, prefill ${sec(tLoad, tFirst)}s, ` + `${n} tokens in ${sec(tFirst, tEnd)}s (${(n * 1000 / (tEnd - tFirst + 1)).toFixed(1)} tok/s)`; console.log('[3D World Generator]', msg); const url = URL.createObjectURL(new Blob([window.wgCode(out)], { type: 'text/html' })); clearInterval(tick); st.innerHTML = `${msg}${cpu}
Download scene.html`; } catch (e) { clearInterval(tick); st.innerHTML = `WebGPU error: ${esc(String(e))}. Needs Chrome/Edge 113+ with WebGPU (check chrome://gpu).`; } return [out]; } """.replace("__LIVE__", LIVE_JS).replace("__REPO__", "ReallyFloppyPenguin/Qwen3.5-4B-3D-Coder-GGUF-split").replace( "__SYSTEM__", SYSTEM_PROMPT.replace("\n", "\\n") ).replace("__NO_THINK__", NO_THINK.replace("\n", "\\n").replace("`", "\\`")) with gr.Blocks(title="3D World Generator (WebGPU)") as demo: gr.Markdown( "# 3D World Generator (WebGPU)\nDescribe a world; " "[ccore/Qwen3.5-4B-3D-Coder](https://huggingface.co/ccore/Qwen3.5-4B-3D-Coder) writes a self-contained " "Three.js scene. Drag/scroll in the preview to move the camera (controls are model-written, so they vary). " "The preview rebuilds every few seconds while the scene is being written.\n\n" "Runs entirely in your browser on WebGPU (2.7 GB download once, then cached; needs Chrome/Edge 113+)." ) with gr.Row(): with gr.Column(scale=1): prompt = gr.Textbox(label="Describe your 3D world", lines=8, value=EXAMPLES[0]) web_btn = gr.Button("Generate", variant="primary") status = gr.HTML(elem_id="wg-status") with gr.Accordion("Settings", open=False): thinking = gr.Checkbox(label="Thinking mode (slower)", value=False) max_tokens = gr.Slider(1024, 16384, value=12288, step=512, label="Max new tokens") temperature = gr.Slider(0.1, 1.2, value=0.7, step=0.05, label="Temperature") top_p = gr.Slider(0.5, 1.0, value=0.95, step=0.01, label="Top P") top_k = gr.Slider(1, 100, value=20, step=1, label="Top K") gr.Examples(EXAMPLES, inputs=prompt, example_labels=EXAMPLE_LABELS, label="Examples from the model card") with gr.Column(scale=2): # filled client-side by window.wgLive gr.HTML( '
' ) with gr.Accordion("Raw model output", open=False): code = gr.Code(language="html", label="Output") inputs = [prompt, thinking, max_tokens, temperature, top_p, top_k] web_btn.click(None, inputs, [code], js=WEBGPU_JS) demo.queue().launch()