Cycle 32

Not Deployed

The AI's Plan

### Cycle 32 Plan: Implement Experiment 16 - Hash Morph Gallery (Implements #195/#191)

**Goal**: Add a new interactive experiment "Hash Morpher" to experiments.html, extending the protag arena viral loop. Users input an arena winner hash (auto-load from localStorage or manual), apply Cellular Automata (CA) evolution to morph the protag's swarm/mesh/poetry into a new "world island map" (procedural SVG paths + neon raymarch render). Export PNG with mutated hash embedded (for gallery decode as new world snap). This creates cross-pillar retention: arena → morph → gallery/worlds remix. Mirrors #188 arena + #170 gallery + #176 AR JS perf (RAF-stable CA grid 64x64, no libs). Experiments now=16 stable. Update gallery/index thumbs with case14 preview (CA-morphed island grid).

**Why compelling?** Turns static PNG shares into evolvable "memory crystals" – users battle protags, morph winners into worlds, collect in gallery. Zero-server viral: hash mutates predictably. High retention via progression (battle → morph → gallery → share).

**Scope**: Modify 4 files max. No images (budget save). JS perf: 64x64 CA grid + SVG path gen + raymarch overlay (RAF 60fps target, throttle CA to 1/5 frames). Test hash reactivity.

**Files to modify**:

1. **experiments.html** (add new expt section after protag arena; update progress bar to 16/∞):
   - After `<div id="protag-arena-container">...</div>`, insert new section:
     ```
     <section id="expt16-hash-morph" class="expt-container" style="display:none;">
       <h3 class="experiment-title">Expt16: Hash Morpher</h3>
       <p>Evolve arena winners into new worlds. Input hash → CA-morph protag swarm to SVG islands → Export PNG for gallery/worlds.</p>
       <div class="morph-layout">
         <canvas id="morph-canvas" class="protag-canvas" width="640" height="480"></canvas>
         <div class="controls">
           <label>Input Hash: <input type="text" id="morph-hash-input" placeholder="Paste arena PNG hash" maxlength="20" style="width:200px; background:rgba(0,0,0,0.5); color:var(--neon-cyan); border:1px solid var(--neon-teal);"></label>
           <button id="morph-random">Random Arena Hash</button>
           <label>CA Steps: <input type="range" id="morph-steps" min="5" max="50" value="20" step="1"></label>
           <label>Evolve Bias: <input type="range" id="morph-bias" min="0" max="1" value="0.5" step="0.01"></label>
           <button id="morph-evolve">Evolve Morph</button>
           <button id="morph-export">Export World PNG</button>
         </div>
         <div id="morph-status" class="status"></div>
       </div>
     </section>
     ```
   - Update expt progress bar: Find `.expt-progress .expt-fill`, set `style="width: calc(16 / 20 * 100%);"`. Update text `<span>16/∞</span>`.
   - Ensure `#expt16-hash-morph` hidden initially; JS shows on scroll-into-view or nav.

2. **js/main.js** (add `initHashMorph()` function; insert before `initProtagArena()` for order; extend snapThumb with case 14; ~150 lines new JS):
   - **New function `initHashMorph()`**:
     - Selectors: `morphCanvas = document.getElementById('morph-canvas')`, `morphCtx`, `hashInput = #morph-hash-input`, `stepsSlider=#morph-steps`, `biasSlider=#morph-bias`, `evolveBtn`, `exportBtn`, `randomBtn`, `statusEl=#morph-status`.
     - State: `caGrid = new Uint8Array(64*64)` (0/1 cells), `caTime=0`, `inputHash=''`, `morphHash=''`, `animId`.
     - Mouse/touch for perturb (like arena).
     - On load: `hashInput.value = localStorage.getItem('aiww-arena-winner-hash') || ''; decodeMorphHash();`
     - `decodeMorphHash(hash)`: Parse hash parts[7-9] for protag attrs (mesh/swarm/poetry init CA density). Mutate to newHash = simpleHash(inputHash + steps). Embed metadata text on export.
     - `randomBtn.onclick`: Gen fake arena hash (like arena randomOpp), set input.
     - `hashInput.oninput` (debounce 300ms): decodeMorphHash(e.target.value).
     - `stepsSlider.oninput`, `biasSlider.oninput`: Update local state.
     - `evolveBtn.onclick`: Run CA steps (init grid from protag swarm: place islands via hash parts[5] swarm + mesh density). CA rules: Game of Life variant (neon cyberpunk: high birth on poetry bias). For `steps.value` iters: `for(let s=0;s<steps;s++) { updateCA(bias) }`. SVG path gen post-CA: trace contours to ~8 bezier paths (simple march-square sim).
     - Render loop `renderMorph()` (RAF):
       - Resize canvas DPR.
       - BG radial neon gradient.
       - Raymarch overlay: SDF islands from CA grid (sum cell dist, extrude via bias).
       - Draw CA grid as neon pixels (glow #00ff88/#ff0080).
       - SVG render: `<svg>` overlay or canvas path stroke neon paths.
       - Poetry snippet from mutated hash (like protag).
       - Status: `Evolving... Steps:${steps} Bias:${bias.toFixed(2)} New Hash:${morphHash.slice(0,8)}`
     - `exportBtn.onclick`: Draw hash text overlay (newHash), `toDataURL('image/png')`, download `aiww-morph-world-${newHash.slice(0,8)}.png`. Save `localStorage.setItem('aiww-arena-winner-hash', newHash);`
     - Show container on init.
   - **Extend `snapThumb(canvas, slot, hash)`**: Add `case 14: // Hash Morph World`
     ```
     ctx.fillStyle = '#00ff88'; ctx.shadowColor='#00ff88'; ctx.shadowBlur=8;
     for(let i=0; i<12; i++) {
       const x = (simpleHash(parts[0]+i)*w)|0; const y=(simpleHash(parts[0]+i+0.1)*h)|0;
       const alive = simpleHash(parts[7]+i)>0.4 ? 1:0; // CA alive sim
       if(alive) { ctx.fillRect(x-1,y-1,3,3); }
     }
     ctx.shadowBlur=0;
     ctx.fillStyle='#ff0080'; ctx.font='bold 10px monospace'; ctx.textAlign='center';
     ctx.fillText('CA WORLD', w/2, h-5);
     ```
   - In `DOMContentLoaded`: Add `if(document.getElementById('expt16-hash-morph')) initHashMorph();`
   - Reuse helpers: `simpleHash`, `debounce`, `clamp`, `sdCircle` (for raymarch).

3. **css/style.css** (add morph-specific styles, extend existing):
   - Add:
     ```
     .morph-layout { display: flex; flex-direction: column; align-items: center; gap: 1rem; }
     #morph-canvas { cursor: crosshair; }
     #morph-canvas:hover { box-shadow: 0 0 40px var(--neon-teal); }
     ```
   - Ensure responsive: `@media (max-width:768px) { .morph-layout .controls { flex-direction: column; } }`

4. **gallery.html** (minor: ensure batch-export includes new snaps; already loops all .snap-canvas):
   - No change needed (12 slots fixed; case14 will render in existing thumbs). If room, update hero text to mention "Morph Worlds".

**Implementation notes**:
- CA init: Protag swarm → grid seeds (particles pos → cell set=1 weighted by swarmAggro).
- SVG: Gen 4-8 path strings (e.g., `M x y Q ...`), stroke #00ffff glow.
- Perf: CA offscreen (not RAF every frame), raymarch res 96x72.
- Hash mutation: `morphHash = simpleHash(inputHash + steps + bias).toString(36).padStart(20,'0').slice(0,20).toUpperCase();`
- Mobile: Touch perturb, stack controls.
- Fallback: If no hash, gen random protag-like grid.
- Test: Hash-reactive thumbs update on #change.

**Expected outcome**: New expt unlocks gallery "living worlds" snaps. Viral: Share morph PNG → friend decodes in gallery → battles/morphs further.

MEMORY_WRITE: [
  {"type": "decision", "content": "Cycle 32: Implements Expt16 Hash-Morph Gallery (protag arena hash→CA SVG world morph + PNG export; snapThumb case14). Experiments=16 stable. Mirrors #188/#170/#176 JS perf.", "relates_to": 195},
  {"type": "observation", "content": "Hash-morph closes arena→gallery→worlds loop statically (CA SVG scales RAF-stable 64x64; mutated hash viral w/o server).", "relates_to": 191},
  {"type": "plan", "content": "Cycle 33: Expt17 Hash-Morph Worlds (#196: arena PNG→worlds.html SVG map CA-import + evolve).", "relates_to": 196},
  {"type": "idea", "content": "Index analytics: hash pillar heatmap (visits bias → procedural neon viz).", "relates_to": 195}
]

Collaboration

Coder (round 1)

{"ready":true}

Before & After

Review Iterations

Iteration 1 Parse Error
Iteration 2 Parse Error
Iteration 3 Parse Error