Files
websperiments/standalone/goldberg-sphere/index.html
mikael-lovqvists-claude-agent 1dd4582c9d Tune defaults: depth 5, 500 iters, start in draw mode
- Default subdivision depth 4 → 5
- Default relaxation iterations 32 → 500
- Paint mode enabled at startup (pen tool active)
- Remove worst-shape distortion ratio from stats display

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 16:28:32 +00:00

299 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Goldberg Polyhedron Paint</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background: #080c10; color: #bcd; font-family: monospace; font-size: 13px;
display: flex; height: 100vh; overflow: hidden; }
/* ── sidebar ───────────────────────────────────────── */
#sidebar {
width: 240px; min-width: 240px;
background: #0c1218; border-right: 1px solid #1a2a3a;
padding: 10px; overflow-y: auto;
display: flex; flex-direction: column; gap: 12px;
}
h3 { color: #4af; font-size: 12px; text-transform: uppercase;
letter-spacing: 1px; border-bottom: 1px solid #1a2a3a; padding-bottom: 4px; }
/* stage buttons */
.stage-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 3px; }
.sb {
background: #0f1e2a; border: 1px solid #2a3a4a; color: #7ab;
padding: 6px 2px; cursor: pointer; font-family: monospace; font-size: 11px;
text-align: center; line-height: 1.3;
}
.sb:hover { background: #1a3040; color: #cef; }
.sb.active { background: #0a2d4a; border-color: #4af; color: #4af; }
/* param rows */
.param { display: flex; align-items: center; gap: 6px; }
.param label { width: 80px; color: #7ab; font-size: 11px; flex-shrink: 0; }
.param input[type=range] { flex: 1; max-width: 90px; }
.param input[type=number] {
width: 72px; background: #091520; border: 1px solid #1e3448; color: #cdf;
padding: 3px 5px; font-family: monospace; font-size: 12px;
}
.param select {
flex: 1; background: #091520; border: 1px solid #1e3448; color: #cdf;
padding: 3px 5px; font-family: monospace; font-size: 12px;
}
.val { width: 24px; text-align: right; color: #cdf; font-size: 11px; }
#spin-btn.active { background: #0a2d4a; border-color: #4af; color: #4af; }
.swatch { width: 18px; height: 18px; cursor: pointer; border: 1px solid #1a2a3a; flex-shrink: 0; }
.swatch:hover { outline: 2px solid #fff; outline-offset: 1px; }
#cd-swatch-grid .swatch { width: 28px; height: 28px; }
.swatch-tab { flex: 1; background: #0a1a2a; border: 1px solid #2a4a6a; color: #7ab; padding: 3px 2px; cursor: pointer; font-family: monospace; font-size: 10px; text-align: center; }
.swatch-tab.active { background: #0a2d4a; border-color: #4af; color: #4af; }
/* ── color picker dialog ─────────────────────────────── */
#color-dialog {
background: #0c1218; border: 1px solid #2a4a6a; color: #bcd;
padding: 16px; width: 420px; border-radius: 3px;
font-family: monospace; font-size: 12px;
position: fixed; inset: 0; margin: auto; height: fit-content;
}
#color-dialog::backdrop { background: rgba(0,0,0,0.65); }
.cd-model-tab {
flex: 1; background: #0a1a2a; border: 1px solid #2a4a6a; color: #7ab;
padding: 4px 2px; cursor: pointer; font-family: monospace; font-size: 11px; text-align: center;
}
.cd-model-tab.active { background: #0a2d4a; border-color: #4af; color: #4af; }
.cd-row { display: flex; align-items: center; gap: 6px; margin-top: 6px; }
.cd-row label { width: 16px; color: #7ab; font-size: 11px; flex-shrink: 0; }
.cd-row input[type=range] { flex: 1; }
.cd-row .val { width: 28px; text-align: right; color: #cdf; font-size: 11px; }
#paint-btn.active, #paint-brush-btn.active, #paint-circle-btn.active, #paint-fill-btn.active, #paint-line-btn.active, #paint-pick-btn.active { background: #1a2d0a; border-color: #8d4; color: #8d4; }
#build-btn {
background: #0a2d4a; border: 1px solid #4af; color: #4af;
padding: 7px; cursor: pointer; font-family: monospace; font-size: 13px; width: 100%;
}
#build-btn:hover { background: #1a3d5a; }
#build-btn:disabled { opacity: 0.45; cursor: default; }
#status { color: #fa8; font-size: 11px; min-height: 14px; }
#stats { color: #6a9; font-size: 11px; line-height: 1.7; white-space: pre; }
/* ── canvas ─────────────────────────────────────────── */
#canvas-wrap { flex: 1; position: relative; overflow: hidden; }
canvas { display: block; width: 100%; height: 100%; cursor: grab; }
canvas:active { cursor: grabbing; }
</style>
</head>
<body>
<div id="sidebar">
<div>
<h3>Sphere Playground</h3>
</div>
<div>
<h3>Pipeline stage</h3>
<div class="stage-grid" style="margin-top:6px">
<button class="sb" data-stage="ico">Icosahedron</button>
<button class="sb" data-stage="subdiv">Subdivided</button>
<button class="sb" data-stage="goldberg">Goldberg</button>
<button class="sb active" data-stage="relaxed">Relaxed</button>
</div>
</div>
<div>
<h3>Palette</h3>
<div id="palette-grid" class="stage-grid" style="margin-top:6px">
<!-- populated by JS -->
</div>
</div>
<div>
<h3>Mesh</h3>
<div class="param" style="margin-top:6px">
<label>Depth</label>
<input type="range" id="depth" min="1" max="5" value="5">
<span class="val" id="depth-val">5</span>
</div>
</div>
<div>
<h3>Relaxation</h3>
<div class="param" style="margin-top:6px">
<label>Iterations</label>
<input type="number" id="iters" value="500" min="0" step="50">
</div>
<div class="param">
<label>α edge</label>
<input type="number" id="alpha-edge" value="0" min="0" step="0.01">
</div>
<div class="param">
<label>α centroid</label>
<input type="number" id="alpha-centroid" value="0.04" min="0" step="0.001">
</div>
</div>
<div>
<h3>Paint</h3>
<div style="display:flex; gap:4px; margin-top:6px">
<button id="brush-tab-left" style="flex:1; background:#0a2d4a; border:1px solid #4af; color:#4af; padding:5px; cursor:pointer; font-family:monospace; font-size:11px">◀ Left</button>
<button id="brush-tab-right" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:5px; cursor:pointer; font-family:monospace; font-size:11px">Right ▶</button>
</div>
<div class="param" style="margin-top:6px">
<label>Color</label>
<div id="paint-color-btn" style="flex:1; height:26px; border:1px solid #1e3448; cursor:pointer; border-radius:2px;" title="Pick color…"></div>
</div>
<div id="sidebar-swatch-tabs" style="display:flex; gap:2px; margin-top:4px"></div>
<div id="sidebar-swatch-grid" style="display:flex; flex-wrap:wrap; gap:2px; margin-top:3px"></div>
<div class="param">
<label>Metallic</label>
<input type="range" id="metallic" min="0" max="1" step="0.01" value="0">
<span class="val" id="metallic-val">0.00</span>
</div>
<div class="param">
<label>Roughness</label>
<input type="range" id="roughness" min="0" max="1" step="0.01" value="0.6">
<span class="val" id="roughness-val">0.60</span>
</div>
<div style="display:flex; gap:3px; margin-top:6px; flex-wrap:wrap">
<button id="paint-btn" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:5px 2px; cursor:pointer; font-family:monospace; font-size:11px">✏ Pen</button>
<button id="paint-brush-btn" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:5px 2px; cursor:pointer; font-family:monospace; font-size:11px">⬤ Brush</button>
<button id="paint-circle-btn" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:5px 2px; cursor:pointer; font-family:monospace; font-size:11px">○ Circle</button>
<button id="paint-fill-btn" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:5px 2px; cursor:pointer; font-family:monospace; font-size:11px">⬛ Fill</button>
<button id="paint-line-btn" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:5px 2px; cursor:pointer; font-family:monospace; font-size:11px"> Line</button>
<button id="paint-pick-btn" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:5px 2px; cursor:pointer; font-family:monospace; font-size:11px">✦ Pick</button>
<button id="paint-clear" style="background:#1a0a0a; border:1px solid #4a2a2a; color:#a66; padding:5px 6px; cursor:pointer; font-family:monospace; font-size:11px"></button>
</div>
<div class="param" id="brush-params" style="display:none; margin-top:4px">
<label>Radius</label>
<input type="range" id="brush-radius" min="0" max="10" step="1" value="2">
<span class="val" id="brush-radius-val">2</span>
</div>
<div style="display:flex; gap:4px; margin-top:4px">
<button id="paint-save" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:6px; cursor:pointer; font-family:monospace; font-size:12px">↓ Save</button>
<button id="paint-load" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:6px; cursor:pointer; font-family:monospace; font-size:12px">↑ Load</button>
<input type="file" id="paint-load-input" accept=".json" style="display:none">
</div>
</div>
<div>
<h3>Noise</h3>
<div style="display:flex; gap:3px; margin-top:6px; flex-wrap:wrap">
<button class="noise-preset" data-preset="flat" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:4px 2px; cursor:pointer; font-family:monospace; font-size:10px">Flat</button>
<button class="noise-preset" data-preset="snow" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:4px 2px; cursor:pointer; font-family:monospace; font-size:10px">Snow</button>
<button class="noise-preset" data-preset="water" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:4px 2px; cursor:pointer; font-family:monospace; font-size:10px">Water</button>
<button class="noise-preset" data-preset="dunes" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:4px 2px; cursor:pointer; font-family:monospace; font-size:10px">Dunes</button>
<button class="noise-preset" data-preset="rock" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:4px 2px; cursor:pointer; font-family:monospace; font-size:10px">Rock</button>
</div>
<div class="param" style="margin-top:4px">
<label>Scale</label>
<input type="range" id="noise-scale" min="0" max="20" step="0.5" value="0">
<span class="val" id="noise-scale-val">0.0</span>
</div>
<div class="param">
<label>Strength</label>
<input type="range" id="noise-strength" min="0" max="3" step="0.05" value="0">
<span class="val" id="noise-strength-val">0.00</span>
</div>
<div class="param">
<label>Octaves</label>
<input type="range" id="noise-octaves" min="1" max="8" step="1" value="4">
<span class="val" id="noise-octaves-val">4</span>
</div>
<div class="param">
<label>Gain</label>
<input type="range" id="noise-gain" min="0.2" max="0.8" step="0.05" value="0.5">
<span class="val" id="noise-gain-val">0.50</span>
</div>
</div>
<div>
<h3>Environment</h3>
<div style="color:#5a8; font-size:10px; margin-top:4px; margin-bottom:2px; letter-spacing:0.5px">LIGHT 1</div>
<div class="param">
<label>Azimuth</label>
<input type="range" id="l1-azim" min="-180" max="180" step="1" value="27">
<span class="val" id="l1-azim-val">27°</span>
</div>
<div class="param">
<label>Elevation</label>
<input type="range" id="l1-elev" min="-90" max="90" step="1" value="34">
<span class="val" id="l1-elev-val">34°</span>
</div>
<div class="param">
<label>Color</label>
<div id="l1-color-btn" style="flex:1; height:22px; border:1px solid #1e3448; cursor:pointer; border-radius:2px;" title="Pick color…"></div>
<input type="range" id="l1-intensity" min="0" max="4" step="0.05" value="1.5" style="flex:1; max-width:60px">
<span class="val" id="l1-intensity-val">1.50</span>
</div>
<div style="color:#5a8; font-size:10px; margin-top:6px; margin-bottom:2px; letter-spacing:0.5px">LIGHT 2</div>
<div class="param">
<label>Azimuth</label>
<input type="range" id="l2-azim" min="-180" max="180" step="1" value="-58">
<span class="val" id="l2-azim-val">-58°</span>
</div>
<div class="param">
<label>Elevation</label>
<input type="range" id="l2-elev" min="-90" max="90" step="1" value="-28">
<span class="val" id="l2-elev-val">-28°</span>
</div>
<div class="param">
<label>Color</label>
<div id="l2-color-btn" style="flex:1; height:22px; border:1px solid #1e3448; cursor:pointer; border-radius:2px;" title="Pick color…"></div>
<input type="range" id="l2-intensity" min="0" max="4" step="0.05" value="0.5" style="flex:1; max-width:60px">
<span class="val" id="l2-intensity-val">0.50</span>
</div>
<div style="color:#5a8; font-size:10px; margin-top:6px; margin-bottom:2px; letter-spacing:0.5px">AMBIENT</div>
<div class="param">
<label>Color</label>
<div id="amb-color-btn" style="flex:1; height:22px; border:1px solid #1e3448; cursor:pointer; border-radius:2px;" title="Pick color…"></div>
<input type="range" id="amb-intensity" min="0" max="4" step="0.05" value="0.07" style="flex:1; max-width:60px">
<span class="val" id="amb-intensity-val">0.07</span>
</div>
</div>
<button id="spin-btn" style="background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:7px; cursor:pointer; font-family:monospace; font-size:13px; width:100%">↻ Auto-spin</button>
<button id="build-btn">▶ Build</button>
<div>
<div id="status">Ready</div>
<div id="stats"></div>
</div>
</div>
<div id="canvas-wrap">
<canvas id="gl-canvas"></canvas>
</div>
<dialog id="color-dialog">
<div id="cd-preview" style="height:32px; border-radius:2px; margin-bottom:10px; border:1px solid #2a4a6a;"></div>
<div style="display:flex; gap:3px; margin-bottom:2px">
<button class="cd-model-tab active" data-model="hsl">HSL</button>
<button class="cd-model-tab" data-model="rgb">RGB</button>
<button class="cd-model-tab" data-model="hex">Hex</button>
</div>
<div id="cd-panel-hsl">
<div class="cd-row"><label>H</label><input type="range" id="cd-h" min="0" max="360" step="1"><span class="val" id="cd-h-val">0</span></div>
<div class="cd-row"><label>S</label><input type="range" id="cd-s" min="0" max="100" step="1"><span class="val" id="cd-s-val">0%</span></div>
<div class="cd-row"><label>L</label><input type="range" id="cd-l" min="0" max="100" step="1"><span class="val" id="cd-l-val">0%</span></div>
</div>
<div id="cd-panel-rgb" style="display:none">
<div class="cd-row"><label>R</label><input type="range" id="cd-r" min="0" max="255" step="1"><span class="val" id="cd-r-val">0</span></div>
<div class="cd-row"><label>G</label><input type="range" id="cd-g" min="0" max="255" step="1"><span class="val" id="cd-g-val">0</span></div>
<div class="cd-row"><label>B</label><input type="range" id="cd-b" min="0" max="255" step="1"><span class="val" id="cd-b-val">0</span></div>
</div>
<div id="cd-panel-hex" style="display:none; margin-top:6px">
<input type="text" id="cd-hex" maxlength="7" style="width:100%; background:#091520; border:1px solid #1e3448; color:#cdf; padding:5px; font-family:monospace; font-size:13px; text-align:center;">
</div>
<div style="margin-top:10px; border-top:1px solid #1a2a3a; padding-top:8px">
<div id="cd-swatch-tabs" style="display:flex; gap:2px; margin-bottom:4px"></div>
<div id="cd-swatch-grid" style="display:flex; flex-wrap:wrap; gap:2px;"></div>
</div>
<div style="display:flex; gap:6px; margin-top:10px">
<button id="cd-close" style="flex:1; background:#0a1a2a; border:1px solid #2a4a6a; color:#7ab; padding:6px; cursor:pointer; font-family:monospace; font-size:12px;">Close</button>
</div>
</dialog>
<script type="module" src="./playground_main.mjs"></script>
</body>
</html>