WorldForge / index.html
openfree's picture
WorldForge: prompt-driven region plan, semantic layout map and composite height field
d9a31a1 verified
Raw
History Blame Contribute Delete
6.94 kB
<!DOCTYPE html>
<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&nbsp;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>