Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>WorldForge — prompt to explorable terrain</title> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <style> | |
| :root { | |
| --ink: #e8eef3; | |
| --dim: #93a3b1; | |
| --panel: #141a20; | |
| --line: #26313b; | |
| --accent: #6fd08c; | |
| } | |
| * { box-sizing: border-box; } | |
| html, body { height: 100%; margin: 0; } | |
| body { | |
| font: 14px/1.5 ui-sans-serif, system-ui, "Segoe UI", Roboto, sans-serif; | |
| color: var(--ink); background: #0c1014; overflow: hidden; | |
| display: grid; grid-template-columns: 320px 1fr; | |
| } | |
| body.busy { cursor: progress; } | |
| aside { | |
| background: var(--panel); border-right: 1px solid var(--line); | |
| padding: 18px; overflow-y: auto; display: flex; flex-direction: column; gap: 16px; | |
| } | |
| h1 { font-size: 17px; margin: 0; letter-spacing: -0.01em; } | |
| h1 span { color: var(--accent); } | |
| .sub { color: var(--dim); font-size: 12px; margin-top: 4px; } | |
| label { display: block; font-size: 11px; text-transform: uppercase; | |
| letter-spacing: 0.08em; color: var(--dim); margin-bottom: 6px; } | |
| input[type=text], input[type=number] { | |
| width: 100%; padding: 9px 10px; border-radius: 7px; | |
| border: 1px solid var(--line); background: #0e1418; color: var(--ink); font: inherit; | |
| } | |
| input:focus { outline: 2px solid var(--accent); outline-offset: -1px; } | |
| .row { display: flex; gap: 8px; } | |
| .row > * { flex: 1; } | |
| button { | |
| padding: 9px 12px; border-radius: 7px; border: 1px solid var(--line); | |
| background: #1b242c; color: var(--ink); font: inherit; cursor: pointer; | |
| } | |
| button:hover { background: #23303a; } | |
| button:disabled { opacity: 0.5; cursor: default; } | |
| button.primary { background: var(--accent); color: #06110a; border-color: var(--accent); font-weight: 600; } | |
| button.primary:hover { background: #82dc9d; } | |
| button.on { background: var(--accent); color: #06110a; border-color: var(--accent); } | |
| #presets { display: flex; flex-wrap: wrap; gap: 6px; } | |
| #presets button { flex: 0 0 auto; padding: 5px 9px; font-size: 12px; border-radius: 20px; } | |
| #layout { width: 100%; aspect-ratio: 1; border-radius: 8px; | |
| border: 1px solid var(--line); image-rendering: pixelated; display: block; } | |
| ul#legend, ul#census { list-style: none; margin: 0; padding: 0; | |
| display: flex; flex-direction: column; gap: 5px; } | |
| #legend li, #census li { display: flex; align-items: center; gap: 8px; font-size: 13px; } | |
| #legend i, #census i { width: 13px; height: 13px; border-radius: 3px; flex: 0 0 auto; | |
| border: 1px solid rgba(255,255,255,.18); } | |
| #legend b, #census b { margin-left: auto; color: var(--dim); font-weight: 500; | |
| font-variant-numeric: tabular-nums; } | |
| input[type=range] { width: 100%; accent-color: var(--accent); } | |
| #clockLabel { color: var(--accent); font-variant-numeric: tabular-nums; letter-spacing: 0; } | |
| #probe { display: flex; flex-direction: column; gap: 4px; font-size: 13px; | |
| background: #0e1418; border: 1px solid var(--line); border-radius: 7px; padding: 9px 10px; } | |
| #probe span { display: flex; gap: 8px; color: var(--dim); } | |
| #probe span b { margin-left: auto; color: var(--ink); font-weight: 500; | |
| font-variant-numeric: tabular-nums; } | |
| #probe > b { color: var(--accent); } | |
| .dimmed { color: var(--dim); } | |
| #stats { color: var(--dim); font-size: 12px; font-variant-numeric: tabular-nums; } | |
| footer { margin-top: auto; color: var(--dim); font-size: 11px; line-height: 1.6; } | |
| footer a { color: #9ec7dd; } | |
| main { position: relative; } | |
| #view { width: 100%; height: 100%; display: block; } | |
| #hint { | |
| display: none; position: absolute; left: 50%; bottom: 24px; transform: translateX(-50%); | |
| background: rgba(10,14,18,.82); border: 1px solid var(--line); border-radius: 8px; | |
| padding: 8px 14px; font-size: 12px; color: var(--dim); | |
| } | |
| @media (max-width: 820px) { | |
| body { grid-template-columns: 1fr; grid-template-rows: auto 1fr; } | |
| aside { max-height: 46vh; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <aside> | |
| <div> | |
| <h1>World<span>Forge</span></h1> | |
| <div class="sub">A prompt becomes a region plan, a semantic layout map, and an explorable height field.</div> | |
| </div> | |
| <div> | |
| <label for="prompt">Prompt</label> | |
| <input id="prompt" type="text" value="a snowy alpine range above a pine forest and a lake" | |
| placeholder="describe a world…" autocomplete="off"> | |
| </div> | |
| <div id="presets"> | |
| <button data-p="a desert of red canyons and mesas">Canyons</button> | |
| <button data-p="volcanic island ringed by ocean and jungle">Volcanic island</button> | |
| <button data-p="snowy alpine peaks over a pine forest">Alps</button> | |
| <button data-p="savanna plains with scattered acacia">Savanna</button> | |
| <button data-p="frozen tundra and glaciers">Tundra</button> | |
| <button data-p="swamp and dense rainforest">Swamp</button> | |
| </div> | |
| <div class="row"> | |
| <div style="flex:1.1"> | |
| <label for="seed">Seed</label> | |
| <input id="seed" type="number" min="1" step="1" value="1"> | |
| </div> | |
| <div style="display:flex; align-items:flex-end"> | |
| <button id="reseed" title="New seed, same prompt">Reroll</button> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <button id="go" class="primary">Generate</button> | |
| <button id="fly" title="WASD + mouse look, Esc to exit">Fly</button> | |
| </div> | |
| <div> | |
| <label>Semantic layout map</label> | |
| <canvas id="layout"></canvas> | |
| </div> | |
| <div> | |
| <label for="clock">Time of day <span id="clockLabel">12:00</span></label> | |
| <input id="clock" type="range" min="0" max="1000" value="420"> | |
| <div class="row" style="margin-top:8px"> | |
| <button id="play">Run day</button> | |
| </div> | |
| </div> | |
| <div> | |
| <label>Regions</label> | |
| <ul id="legend"></ul> | |
| </div> | |
| <div> | |
| <label>Inhabitants</label> | |
| <ul id="census"></ul> | |
| </div> | |
| <div> | |
| <label>Inspect</label> | |
| <div id="probe"><span class="dimmed">click the ground to inspect</span></div> | |
| </div> | |
| <div id="stats"></div> | |
| <div class="row"> | |
| <button id="glb">Export GLB</button> | |
| <button id="json">World spec</button> | |
| <button id="png">Map</button> | |
| </div> | |
| <footer> | |
| An independent implementation of the terrain foundation described in | |
| <a href="https://arxiv.org/abs/2608.05248" target="_blank" rel="noopener">WorldClaw</a> | |
| (arXiv 2608.05248). That project publishes no code or weights; nothing from it is | |
| used here. Everything runs in your browser.<br> | |
| Built by <a href="https://huggingface.co/VIDraft" target="_blank" rel="noopener">VIDraft</a>. | |
| </footer> | |
| </aside> | |
| <main> | |
| <canvas id="view"></canvas> | |
| <div id="hint">WASD move · mouse look · Q/E down/up · Shift boost · Esc exit</div> | |
| </main> | |
| <script type="importmap"> | |
| { "imports": { "three": "./vendor/three.module.js" } } | |
| </script> | |
| <script type="module" src="./app.js"></script> | |
| </body> | |
| </html> | |