templates/viewer.html
templates/viewer.htmlBrowse 17 files
3,063 tokens
10,858 bytes
Token encoding: o200k_base
Snapshot 24fd22b
← Back to SKILL.md
1<!DOCTYPE html>2<!--3 p5.js Interactive Viewer Template4 =================================5 USE THIS AS THE STARTING POINT for interactive generative art sketches.6 7 FIXED (keep as-is):8 ✓ Layout structure (sidebar + canvas)9 ✓ Seed navigation (prev/next/random/jump)10 ✓ Action buttons (regenerate, reset, download PNG)11 ✓ Responsive canvas sizing12 ✓ Parameter update + regeneration wiring13 14 VARIABLE (replace for each project):15 ✗ The p5.js algorithm (setup/draw/classes)16 ✗ The PARAMS object (define what your art needs)17 ✗ The parameter controls in the sidebar (sliders, pickers)18 ✗ The color palette19 ✗ The title and description20 21 For headless export: add noLoop() and window._p5Ready=true in setup().22-->23<html lang="en">24<head>25<meta charset="UTF-8">26<meta name="viewport" content="width=device-width, initial-scale=1.0">27<title>Generative Art Viewer</title>28<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.3/p5.min.js"></script>29<style>30 * { margin: 0; padding: 0; box-sizing: border-box; }31 body {32 font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;33 background: #0a0a0f;34 color: #c8c8d0;35 display: flex;36 min-height: 100vh;37 overflow: hidden;38 }39 40 /* --- Sidebar --- */41 .sidebar {42 width: 280px;43 flex-shrink: 0;44 background: #12121a;45 border-right: 1px solid #1e1e2a;46 padding: 20px;47 overflow-y: auto;48 display: flex;49 flex-direction: column;50 gap: 20px;51 }52 .sidebar h1 {53 font-size: 18px;54 font-weight: 600;55 color: #e8e8f0;56 margin-bottom: 4px;57 }58 .sidebar .subtitle {59 font-size: 12px;60 color: #666;61 margin-bottom: 8px;62 }63 .section-title {64 font-size: 11px;65 font-weight: 600;66 text-transform: uppercase;67 letter-spacing: 1px;68 color: #555;69 margin-bottom: 8px;70 }71 72 /* --- Seed Controls --- */73 .seed-display {74 font-family: 'SF Mono', 'Fira Code', monospace;75 font-size: 24px;76 font-weight: 700;77 color: #e8e8f0;78 text-align: center;79 padding: 8px;80 background: #1a1a25;81 border-radius: 6px;82 margin-bottom: 8px;83 }84 .seed-nav {85 display: flex;86 gap: 6px;87 margin-bottom: 6px;88 }89 .seed-nav button {90 flex: 1;91 padding: 6px;92 font-size: 12px;93 }94 .seed-jump {95 display: flex;96 gap: 6px;97 }98 .seed-jump input {99 flex: 1;100 padding: 6px 8px;101 background: #1a1a25;102 border: 1px solid #2a2a35;103 border-radius: 4px;104 color: #c8c8d0;105 font-size: 12px;106 font-family: monospace;107 }108 .seed-jump button { padding: 6px 12px; font-size: 12px; }109 110 /* --- Parameter Controls --- */111 .control-group {112 margin-bottom: 12px;113 }114 .control-group label {115 display: flex;116 justify-content: space-between;117 font-size: 12px;118 color: #888;119 margin-bottom: 4px;120 }121 .control-group .value {122 color: #aaa;123 font-family: monospace;124 font-size: 11px;125 }126 .control-group input[type="range"] {127 width: 100%;128 height: 4px;129 -webkit-appearance: none;130 background: #2a2a35;131 border-radius: 2px;132 outline: none;133 }134 .control-group input[type="range"]::-webkit-slider-thumb {135 -webkit-appearance: none;136 width: 14px; height: 14px;137 border-radius: 50%;138 background: #6a9bcc;139 cursor: pointer;140 }141 .control-group input[type="color"] {142 width: 100%;143 height: 28px;144 border: 1px solid #2a2a35;145 border-radius: 4px;146 background: #1a1a25;147 cursor: pointer;148 }149 150 /* --- Buttons --- */151 button {152 padding: 8px 12px;153 background: #1e1e2a;154 border: 1px solid #2a2a35;155 border-radius: 4px;156 color: #c8c8d0;157 font-size: 12px;158 cursor: pointer;159 transition: background 0.15s;160 }161 button:hover { background: #2a2a3a; }162 button.primary { background: #2a4a6a; border-color: #3a5a7a; }163 button.primary:hover { background: #3a5a7a; }164 165 .actions { display: flex; flex-direction: column; gap: 6px; }166 .actions button { width: 100%; }167 168 /* --- Canvas Area --- */169 .canvas-area {170 flex: 1;171 display: flex;172 align-items: center;173 justify-content: center;174 padding: 20px;175 background: #08080c;176 }177 canvas { display: block; }178</style>179</head>180<body>181 182<!-- === SIDEBAR === -->183<div class="sidebar">184 <!-- FIXED: Title (customize text, keep structure) -->185 <div>186 <h1 id="art-title">Generative Sketch</h1>187 <div class="subtitle" id="art-subtitle">p5.js generative art</div>188 </div>189 190 <!-- FIXED: Seed Navigation -->191 <div>192 <div class="section-title">Seed</div>193 <div class="seed-display" id="seed-display">42</div>194 <div class="seed-nav">195 <button onclick="changeSeed(-1)">◀ Prev</button>196 <button onclick="changeSeed(1)">Next ▶</button>197 <button onclick="randomizeSeed()">Random</button>198 </div>199 <div class="seed-jump">200 <input type="number" id="seed-input" placeholder="Seed #" min="0">201 <button onclick="jumpToSeed()">Go</button>202 </div>203 </div>204 205 <!-- VARIABLE: Parameters (customize for each project) -->206 <div id="params-section">207 <div class="section-title">Parameters</div>208 209 <!-- === REPLACE THESE WITH YOUR PARAMETERS === -->210 <div class="control-group">211 <label>Count <span class="value" id="count-val">500</span></label>212 <input type="range" id="count" min="50" max="2000" step="50" value="500"213 oninput="updateParam('count', +this.value)">214 </div>215 216 <div class="control-group">217 <label>Scale <span class="value" id="scale-val">0.005</span></label>218 <input type="range" id="scale" min="0.001" max="0.02" step="0.001" value="0.005"219 oninput="updateParam('scale', +this.value)">220 </div>221 222 <div class="control-group">223 <label>Speed <span class="value" id="speed-val">2.0</span></label>224 <input type="range" id="speed" min="0.5" max="5" step="0.1" value="2.0"225 oninput="updateParam('speed', +this.value)">226 </div>227 <!-- === END PARAMETER CONTROLS === -->228 </div>229 230 <!-- VARIABLE: Colors (optional — include if art needs adjustable palette) -->231 <!--232 <div>233 <div class="section-title">Colors</div>234 <div class="control-group">235 <label>Background</label>236 <input type="color" id="bg-color" value="#0a0a14"237 oninput="updateParam('bgColor', this.value)">238 </div>239 <div class="control-group">240 <label>Primary</label>241 <input type="color" id="primary-color" value="#6a9bcc"242 oninput="updateParam('primaryColor', this.value)">243 </div>244 </div>245 -->246 247 <!-- FIXED: Actions -->248 <div class="actions">249 <div class="section-title">Actions</div>250 <button class="primary" onclick="regenerate()">Regenerate</button>251 <button onclick="resetDefaults()">Reset Defaults</button>252 <button onclick="downloadPNG()">Download PNG</button>253 </div>254</div>255 256<!-- === CANVAS === -->257<div class="canvas-area" id="canvas-container"></div>258 259<script>260// ====================================================================261// CONFIGURATION — REPLACE FOR EACH PROJECT262// ====================================================================263const DEFAULTS = {264 seed: 42,265 count: 500,266 scale: 0.005,267 speed: 2.0,268 // Add your parameters here269};270 271let PARAMS = { ...DEFAULTS };272 273// ====================================================================274// SEED NAVIGATION — FIXED (do not modify)275// ====================================================================276function changeSeed(delta) {277 PARAMS.seed = Math.max(0, PARAMS.seed + delta);278 document.getElementById('seed-display').textContent = PARAMS.seed;279 regenerate();280}281 282function randomizeSeed() {283 PARAMS.seed = Math.floor(Math.random() * 99999);284 document.getElementById('seed-display').textContent = PARAMS.seed;285 regenerate();286}287 288function jumpToSeed() {289 let v = parseInt(document.getElementById('seed-input').value);290 if (!isNaN(v) && v >= 0) {291 PARAMS.seed = v;292 document.getElementById('seed-display').textContent = PARAMS.seed;293 document.getElementById('seed-input').value = '';294 regenerate();295 }296}297 298// ====================================================================299// PARAMETER UPDATES — CUSTOMIZE updateParam body as needed300// ====================================================================301function updateParam(name, value) {302 PARAMS[name] = value;303 let el = document.getElementById(name + '-val');304 if (el) el.textContent = typeof value === 'number' && value < 1 ? value.toFixed(3) : value;305 regenerate();306}307 308function resetDefaults() {309 PARAMS = { ...DEFAULTS };310 // Reset all sliders to default values311 for (let [key, val] of Object.entries(DEFAULTS)) {312 let el = document.getElementById(key);313 if (el) el.value = val;314 let valEl = document.getElementById(key + '-val');315 if (valEl) valEl.textContent = typeof val === 'number' && val < 1 ? val.toFixed(3) : val;316 }317 document.getElementById('seed-display').textContent = PARAMS.seed;318 regenerate();319}320 321function regenerate() {322 randomSeed(PARAMS.seed);323 noiseSeed(PARAMS.seed);324 // Clear and redraw325 clear();326 initializeArt();327 redraw();328}329 330function downloadPNG() {331 saveCanvas('generative-art-seed-' + PARAMS.seed, 'png');332}333 334// ====================================================================335// P5.JS SKETCH — REPLACE ENTIRELY FOR EACH PROJECT336// ====================================================================337 338// Your state variables339let particles = [];340 341function initializeArt() {342 // Initialize your generative system using PARAMS343 // This is called on every regenerate()344 particles = [];345 for (let i = 0; i < PARAMS.count; i++) {346 particles.push({347 x: random(width),348 y: random(height),349 vx: 0, vy: 0350 });351 }352}353 354function setup() {355 // Size canvas to fit container356 let container = document.getElementById('canvas-container');357 let size = Math.min(container.clientWidth - 40, container.clientHeight - 40, 1080);358 let cnv = createCanvas(size, size);359 cnv.parent('canvas-container');360 pixelDensity(1);361 colorMode(HSB, 360, 100, 100, 100);362 363 randomSeed(PARAMS.seed);364 noiseSeed(PARAMS.seed);365 initializeArt();366 367 // For interactive/animated sketches: remove noLoop()368 // For static generation: keep noLoop()369 noLoop();370}371 372function draw() {373 background(0, 0, 5);374 375 // === YOUR ALGORITHM HERE ===376 // Use PARAMS.count, PARAMS.scale, PARAMS.speed, etc.377 noStroke();378 for (let p of particles) {379 let n = noise(p.x * PARAMS.scale, p.y * PARAMS.scale);380 let hue = (n * 200 + PARAMS.seed * 0.1) % 360;381 fill(hue, 70, 80, 60);382 circle(p.x, p.y, n * 10 + 2);383 }384 // === END ALGORITHM ===385}386 387function windowResized() {388 let container = document.getElementById('canvas-container');389 let size = Math.min(container.clientWidth - 40, container.clientHeight - 40, 1080);390 resizeCanvas(size, size);391 regenerate();392}393</script>394</body>395</html>Referenced from SKILL.md
SKILL.mdView in source ↗
Source excerpt starting at line 167.SKILL.mdView in source ↗167- **Interaction model** — passive (no input), mouse-driven, keyboard-driven, audio-reactive, scroll-driven168- **Viewer UI** — for interactive generative art, start from `templates/viewer.html` which provides seed navigation, parameter sliders, and download. For simple sketches or video export, use bare HTML
Source excerpt starting at line 172.SKILL.mdView in source ↗172For **interactive generative art** (seed exploration, parameter tuning): start from `templates/viewer.html`. Read the template first, keep the fixed sections (seed nav, actions), replace the algorithm and parameter controls. This gives the user seed prev/next/random/jump, parameter sliders with live update, and PNG download — all wired up.
Source excerpt starting at line 523.523| `references/troubleshooting.md` | Performance profiling, per-pixel budgets, common mistakes, browser compatibility, WebGL debugging, font loading issues, pixel density traps, memory leaks, CORS |524| `templates/viewer.html` | Interactive viewer template: seed navigation (prev/next/random/jump), parameter sliders, download PNG, responsive canvas. Start from this for explorable generative art |