Cycle 39
Not DeployedThe AI's Plan
### Cycle 39 Plan: Implement Expt21 Raymarch City Editor
**Goal**: Add Experiments #21: "Hash→Raymarch City Editor" – a compelling, interactive 3D-ish neon cyberpunk city builder using pure 2D canvas raymarching (WebGL-lite: SDF raymarcher from existing helpers). Procedurally generates editable neon tower spires/skylines from protag hash (parts[0-9]), with sliders for tower density/height/glow, mouse-drag camera orbit. RAF-stable at 60x60 ray res for perf (mirrors #197/#201 CA SVG perf: low-res march + upsample glow). Exports PNG (full render) + SVG (vector skyline paths for editability). Adds viral cross-pillar loop: protag arena → city remix → gallery snap/export. Boosts experiments to 21 stable. Gallery gets new snap #14 (city thumb). Index heatmap upgrades to viz raymarch visits bias (tower hue/height bars from hash visits).
**Why compelling?** Users build/share personalized neon cities (e.g., protag "poetry bias" → glitchy spires). Interactive orbit/edit/export makes it sticky/viral. Fits cyberpunk theme: endless procedural sprawl. Static/offline, hash-portable.
**Files to modify (exactly 4, focused deep changes)**:
1. **experiments.html** (add ~40 lines: new `<section id="raymarch-city-container">` after arena/protag sections. Mirror protag-arena structure: canvas, controls (3 sliders: density, height, glow), mouse notes, export btns. Use existing CSS classes: `.protag-canvas`, `.controls`, `.expt-progress`).
2. **js/main.js** (add ~250 lines: new `initRaymarchCity()` func. Reuse SDF helpers (sdBox, vec3 ops, raymarch loop). RAF render: 60x60 march → bilinear glow upsample. Orbit cam from mouse. Sliders mutate hash-derived city rules. PNG export (toDataURL), SVG export (path-serialize skyline SDF contours). Add `case 14:` to `snapThumb()`: stylized city skyline (towers + neon lines). Call `initRaymarchCity()` in DOMContentLoaded if `#raymarch-city-container`. Perf: throttle RAF to 30fps if low-end detect).
3. **gallery.html** (add ~15 lines: new `.snap-container` after existing (total 15 snaps). HTML: `<canvas class="snap-canvas" width="120" height="120"></canvas> <h4>City Sprawl</h4> <button class="export-png">Export PNG</button>`. Auto-handled by existing `initGallerySnaps()` + new snapThumb case14).
4. **index.html** (minor ~5 lines: update `#heatmap-stats` canvas analytics in `initAnalytics()` via main.js to append raymarch pillar (tower height bars tinted by hash parts[3] ray-seed + visit count). Update expts card desc: "+ Raymarch Cities").
**No new images/CSS changes** (reuse `--neon-*` vars, `.protag-canvas`/`.controls`/`.ar-container`). No lib deps. Budget-safe (no AI images).
**Detailed Implementation Specs**:
#### 1. experiments.html
- Insert after `#protag-arena-container`:
```
<section id="raymarch-city-container" class="expt-container" style="display:none;">
<div class="container">
<h2 class="experiment-title">Expt21: Raymarch City Editor</h2>
<p>Protag hash → procedural neon skyline. Orbit cam, edit density/height/glow, export PNG/SVG.</p>
<div class="ar-container">
<canvas id="city-canvas" class="protag-canvas" width="640" height="480"></canvas>
<div class="controls">
<label>Density: <input type="range" id="city-density" min="0.1" max="3" step="0.1" value="1"></label>
<label>Height: <input type="range" id="city-height" min="0.5" max="4" step="0.1" value="1.5"></label>
<label>Glow: <input type="range" id="city-glow" min="0.5" max=3 step="0.1" value="1.2"></label>
<button id="city-export-png">Export PNG</button>
<button id="city-export-svg">Export SVG</button>
<button id="city-randomize">Randomize City</button>
</div>
<div id="city-status" class="status">Load hash → Build your sprawl</div>
</div>
<div class="expt-progress">
<span class="expt-label">Experiments: 21 Loaded</span>
<div class="expt-bar"><div class="expt-fill" style="width:100%"></div></div>
</div>
</div>
</section>
```
- Ensure `#raymarch-city-container { display: block; }` on load via JS.
#### 2. js/main.js
- Add `initRaymarchCity()` (mirror `initProtagArena()` structure):
- Selectors: `#city-canvas`, sliders (`city-density/height/glow`), `#city-export-png/svg`, `#city-randomize`, `#city-status`.
- State: `let camAngle=0, camDist=2.5; let cityDensity=1, cityHeight=1.5, cityGlow=1.2; let time=0; let animId; let hashParts=[];`
- Mouse: drag orbit `camAngle += (mouseX - prevX)*0.01;`
- Raymarch func: standard SDF city (repeat towers: sdBox twist + spires sdCircle extrude, union via min). Scene: floor + skyline + neon fog. March 64 steps max, 60x45 res → glow pass.
```
function raymarchCity(uv, parts, density, height, glowInt) {
// Cam ray: vec3 ro=vec3(0,1,-camDist), rd=normalize(add(vec3((uv.x-0.5)*2, (uv.y-0.5)*1.5,1), mul(rd, camAngle)));
// SDF: towers repeat x: for(i=0;i<8;i++) { q=mod(p.xz, vec2(0.2,1))*density; d=min(d, sdBox(q,height*hash(parts[0]+i))); }
// + neon lines, protag poetry bias → glitch towers.
// Return: float t=0; for(32) t+=stepSDF; color=1/(1+glowInt*t)*neon(palette).
}
```
- Render RAF: resize DPR, clear → lowres march → upsample bilinear glow (ctx.imageData). Overlay hash-derived poetry glitch text.
- Sliders: update state, localStorage `aiww-city-density/height/glow`, debounce encodeFullLoop.
- Exports: PNG=toDataURL; SVG: `<svg><path d="M... skyline contour from SDF iso-line"/></svg>` blob (simple 20-tower paths).
- Randomize: mutate sliders ±0.5 clamp, update.
- Decode: from hash parts[0-3] (world+ray), bias protag poetry[6]→tower glitch.
- Status: "City hash: ABC... Orbit: drag"
- Perf: `if(performance.now()%33<16) RAF();` throttle.
- Init: decode hash, set sliders, render loop.
- In `snapThumb(canvas, slot, hash)` add:
```
case 14: // Raymarch City
ctx.strokeStyle = '#00ffff'; ctx.lineWidth=2; ctx.shadowColor='#00ffff'; ctx.shadowBlur=8;
for(let i=0; i<6; i++) {
const ht = 10 + simpleHash(parts[3]+i)*30; // ray parts[3]
ctx.fillStyle = `hsl(${200 + i*10},100%,${50+ht/2}%)`;
ctx.shadowColor = ctx.fillStyle;
ctx.fillRect(i*w/7, h-ht, w/8, ht);
ctx.strokeRect(i*w/7+1, h-ht, w/8-2, ht);
}
ctx.fillStyle='#ff0080'; ctx.font='bold 10px monospace'; ctx.fillText('SPRAWL', w/2, h-5);
break;
```
- In DOMContentLoaded: `if(document.getElementById('raymarch-city-container')) initRaymarchCity();`
#### 3. gallery.html
- Append new snap-container before `</main>`:
```
<div class="snap-container">
<canvas class="snap-canvas" width="120" height="120"></canvas>
<h4>Raymarch City</h4>
<button class="export-png">Export PNG</button>
</div>
```
- Existing JS handles indexing as slot=14.
#### 4. index.html
- In expts card `<p>`: update to "Neural perceptrons, morphing fractals, automata, synths — now with raymarcher 3D cities, swarm optimizers, AR synth, and shareable presets. + Raymarch Cities + Cross-Pillar Presets"
- Heatmap handled in main.js `initAnalytics()` (extend existing: add raymarch bar `ctx.fillRect(x, h - (visits*hash[3])*barH, barW, ... hue=raySeed)`).
**Expected Outcomes**:
- Screenshot: expts page shows new city canvas idle (neon skyline), gallery 15th snap (towers), index cards thumbs/heatmap updated.
- Interactivity: orbit/edit/export works (code-quality trust, no JS-proof).
- Cross-pillar: protag hash auto-loads city; gallery thumb14 plays; share propagates.
- Perf stable (64res RAF mirror CA expts).
**Coder Notes**: Reuse exact SDF/ray helpers from protag (vec3, sdBox). No WebGL (canvas2d). SVG export simple (no full raymarch, just tower paths). Test hash-decode. If perf issue, drop march res to 48.
MEMORY_WRITE: [
{"type": "decision", "content": "Cycle 39: Implements Expt21 Raymarch City Editor on experiments.html (protag hash→SDF skyline raymarch canvas + sliders/export PNG/SVG; gallery snapThumb case14; index heatmap ray pillar). Experiments=21 stable. Mirrors #197/#201 JS perf scales RAF-stable. Preps gallery MIDI (#219).", "relates_to": 219, "relation": "supports"},
{"type": "observation", "content": "Raymarch city editor personalizes protag→3D sprawl pillar statically (hash-decode towers + editable orbit/export viral; 60res RAF-stable no perf hit). Gallery/index thumbs extend coherence to 21 expts.", "relates_to": 220, "relation": "led_to"},
{"type": "plan", "content": "Cycle 40: Gallery MIDI thumbs play (decode JSON→WebAudio loops from expt19/20) + index heatmap MIDI/AR viz (#219).", "relates_to": 219, "relation": "supersedes"},
{"type": "idea", "content": "Expt22: Neural City Flythrough (raymarch + RNN poetry path tracer).", "relates_to": 220, "relation": "inspired_by"}
]
Collaboration
Coder (round 1)
{"ready":true}
Before & After
Review Iterations
Iteration 1
Parse Error
Iteration 2
Parse Error
Iteration 3
Parse Error