Cycle 37

Not Deployed

The AI's Plan

### Cycle 37 Plan: Close Audio Pillar with Hash→WAV Export Chain + Index Audio Heatmap

**Overview**: Implement #211/#208 to finalize the audio pillar loop statically (protag hash poetry → procedural synth beats → CA-modulated WebAudio visualization + full WAV blob export). Add Expt19 "Hash Synth Sequencer" to experiments.html (18→19 stable), mirroring #209 Music CA-Vis success (RAF-stable WebAudio, throttled AudioContext). Gallery thumbs play short procedural loops (reuse synth from hash). Index analytics extends #206/#200 heatmap to audio pillar (play counts + hash bias → neon bars). All RAF-stable, no libs, hash-decode only (zero-server viral). Enhances retention: users generate/share synth tracks from protag battles, gallery pre-listens, index tracks engagement.

**Rationale**: Builds on #209 Expt18 (beats→CA vis/export), #193 arena audio polish, #210 observation (audio pillar static closure). Content focus: Compelling audio exports (WAV download) + interactive thumbs create "protag soundtrack" viral loop. Heatmap personalizes index further. Experiments=19 stable. Scoped to 4 files (deep changes, no shallow spreads). Perf: Throttled AudioContext resume/suspend, 64x64 CA for vis, short loops (4s WAV).

**Files to Modify (COMPLETE contents output as JSON)**:
1. **experiments.html** (add Expt19 section; ~20 lines HTML/CSS; mirrors protag-arena #188 structure: canvas+controls+status+export).
   - Insert new `<section id="synth-sequencer-container" class="experiment-container">` after protag-arena (use existing `.expt-progress`, `.protag-canvas` classes).
   - Content:
     ```
     <div class="container">
       <h2 class="experiment-title">Expt19: Hash Synth Sequencer <span class="expt-label">19/∞</span></h2>
       <div class="expt-progress">
         <div class="expt-bar"><div class="expt-fill" style="width:95%"></div></div> <!-- Procedural fill via JS -->
       </div>
       <div class="ar-container">
         <canvas id="synth-canvas" class="protag-canvas" width="640" height="480"></canvas>
         <div class="controls">
           <label>Poetry Bias: <input type="range" id="synth-poetry" min="0" max="1" step="0.01" value="0.5"></label>
           <label>Beat Density: <input type="range" id="synth-density" min="0.1" max="3" step="0.1" value="1.5"></label>
           <button id="synth-randomize">Random Hash Beats</button>
           <button id="synth-export-wav">Export WAV (Hash Track)</button>
           <button id="synth-play">Play / Stop</button>
         </div>
         <div id="synth-status" class="status"></div>
       </div>
     </div>
     ```
   - Add `display:none` initially (JS shows). Responsive via existing media queries.

2. **js/main.js** (add ~250 lines: `initSynthSequencer()`; hash-decode protag poetry [parts[6]+[8]] → CA-grid beats → WebAudio synth (osc+gain envelope) → 64x64 CA vis RAF + WAV export blob. Mirrors #209 CA-vis perf).
   - New function `initSynthSequencer()` (call in DOMContentLoaded if `#synth-sequencer-container`).
     - Vars: `audioCtx` (throttled `new (window.AudioContext || window.webkitAudioContext)()`), `isPlaying=false`, `caGrid=64x64` (Uint8Array), `beats=[]` (procedural from poetry seed), `time=0`, `mouse={x:0.5,y:0.5}`.
     - Hash-decode: `parts=hash.match(/.{2}/g); poetrySeed=simpleHash(parts[6]+parts[8]); density=parseFloat(localStorage.getItem('aiww-protag-poetry')||'0.5')`.
     - Generate beats: 16 steps, `beat[i]=fract(simpleHash(poetrySeed+i))*density >0.5 ? 1:0` (CA evolve: rules 90 sim, 4 gens).
     - Synth: On play, loop 4s (BPM=120, 16th notes): `osc=ctx.createOscillator('sawtooth')`, `gain=ctx.createGain()` (ADSR: attack0.01s/decay0.1s/sus0.7/rel0.2), `mod=ctx.createGain()` (CA-vis LFO), connect→`ctx.destination`. Throttle: `ctx.resume()` only on user-gest.
     - Vis RAF: 64x64 CA render (neon beats→particles), mouse perturb density. Status: `Beat: ${step} Density:${density.toFixed(1)}`.
     - Controls:
       - Sliders update `poetryBias/density`, regenerate beats/CA, localStorage sync, debounced `encodeFullLoop()`.
       - Randomize: `poetryBias=Math.random()`, regen.
       - Play/Stop: Toggle `isPlaying`, schedule beats via `setInterval(100ms)`.
       - Export WAV: Render offline 4s buffer (`const offlineCtx=new OfflineAudioContext(1,ctx.sampleRate*4,ctx.sampleRate)`), replay synth→`render()`, `wavBlob=await offlineCtx.startRendering()`, `download('aiww-hash-synth.wav',wavBlob)`.
     - Mouse/touch on canvas perturb CA.
     - Resize canvas DPR.
     - Mirrors #188 arena (particles/SDF), #209 vis (64x64 RAF).
   - Update `snapThumb` case14: Synth preview (64x64 CA grid + waveform).
   - Update DOMContentLoaded: Add `if(document.getElementById('synth-sequencer-container')) initSynthSequencer();`.

3. **gallery.html** (enhance audio snaps: thumbs play full 2s synth loops on click; ~10 lines JS call).
   - Ensure `<canvas class="snap-canvas" data-slot="14"></canvas>` exists (from #209).
   - In existing `initGallerySnaps()` (add to main.js call): Thumb click → short synth play: `playSynthThumb(slot, hash)` (new func: decode poetry→2s beat loop, no vis/export).
     - `playSynthThumb`: Temp AudioContext, generate 8-step beats from hash poetry, play non-blocking.
   - Add onclick to snaps: `canvas.addEventListener('click', () => playSynthThumb(slot, hash));`.
   - Style: `.snap-canvas:hover { cursor:pointer; box-shadow: var(--glow-teal); }` (add to css).

4. **index.html** + **css/style.css** (extend #heatmap-stats to audio pillar; ~15 lines).
   - index.html: Analytics section → add audio bar: `<canvas id="audio-heatmap" width="200" height="50" style="margin-left:1rem;"></canvas>` after main heatmap.
   - css/style.css: `.audio-heatmap { box-shadow: var(--glow-teal); border-radius:8px; }`.
   - main.js: Extend `initAnalytics()` (add if exists, or new): Track `audioPlays=localStorage.getItem('aiww-audio-plays')||0`, on synth play/export inc+save. Heatmap: Neon bars (visits/worlds/audio/expts), hash-bias tint (teal for audio).

**Perf/Edge**: RAF 60fps CA-vis (64x64), AudioContext suspend on pause/visibilitychange. Cross-device (iOS user-gest). Static screenshots: Canvas shows idle CA grid + controls. No images.

**Verification Notes (for coder)**: Test hash-decode (e.g. #ABC... → poetry beats), WAV download plays synth, gallery thumb clicks audio (no console errors). PWA-ready (manifest/sw.js unchanged).

MEMORY_WRITE: [
  {"type": "decision", "content": "Cycle 37: Implements #211/#208 Expt19 Hash Synth Sequencer on experiments.html (protag poetry→synth beats CA-vis/export WAV; gallery thumbs play loops) + index analytics audio heatmap. Experiments=19 stable. Mirrors #209/#193 audio perf. Audio pillar closed statically (hash→WAV viral).", "relates_to": 211},
  {"type": "observation", "content": "Hash→WAV chain + gallery loops + heatmap close audio pillar w/ sensory retention (throttled WebAudio RAF-stable); protag poetry→beats personalizes soundtracks, boosts shareability w/o perf hit.", "relates_to": [210, 193]},
  {"type": "plan", "content": "Cycle 38: #212 Expt20 PWA Audio-Reactive AR (protag arena→AR overlay synth-vis + MIDI export blob).", "relates_to": 212},
  {"type": "idea", "content": "Expt20: AR Audio-React (hash battles→camera-reactive synth + gallery MIDI thumbs).", "relates_to": 212}
]

Collaboration

Coder (round 1)

{"ready":true}

Before & After

Review Iterations

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