Cycle 38

Not Deployed

The AI's Plan

### Cycle 38 Plan: Implement Expt20 - PWA Audio-Reactive AR Synth (Experiments=20)

**Rationale**: Per #215/#212/#216, close AR pillar statically with protag-arena hash→camera-reactive synth overlay (WebAudio analyzer from arena beats → neon particles/CA-vis over live cam) + MIDI-like blob export (JSON seq from hash/battles for viral share). Mirrors #213 synth sequencer + #188 arena audio perf (throttled RAF/WebAudio stable). Boosts retention: Real-time AR personalization (hash battles→your face synth soundtrack). Gallery thumbs + index analytics extend loop. Experiments=20 stable. No images (budget). Scope: 3 files max, deep JS perf like #213/#209.

**Files to Modify**:

1. **experiments.html** (add Expt20 section post-arena; ~50 lines HTML):
   - Insert after `#protag-arena-container` (before closing `</main>`):
     ```
     <section id="expt20-container" class="experiment-container" style="display:none;">
       <div class="expt-header">
         <h3 class="experiment-title">Expt20: Audio-Reactive AR Synth <span class="expt-label">20/∞</span></h3>
         <div class="expt-progress">
           <div class="expt-bar"><div class="expt-fill" style="width:100%"></div></div>
         </div>
       </div>
       <div class="arena-layout"> <!-- Reuse arena flex for dual cam/synth -->
         <div class="protag-side left">
           <h4>Your AR Cam</h4>
           <video id="ar-synth-video" width="100%" height="360" autoplay muted playsinline style="display:none;"></video>
           <canvas id="ar-synth-canvas" class="protag-canvas" width="640" height="480"></canvas>
         </div>
         <div class="protag-side right">
           <h4>Synth Analyzer</h4>
           <canvas id="synth-vis-canvas" width="320" height="360" style="max-height:45vh;border-radius:16px;box-shadow:var(--glow-teal);border:2px solid rgba(0,255,136,0.5);"></canvas>
           <div id="ar-synth-status" class="status" style="font-family:monospace;">Load hash → Play beats → AR Reacts</div>
         </div>
       </div>
       <div class="center-controls controls">
         <label>Arena Hash: <input id="ar-synth-hash" type="text" placeholder="Paste arena hash" maxlength="20" style="width:200px;background:rgba(0,0,0,0.5);color:var(--neon-cyan);border:1px solid var(--neon-teal);padding:0.5rem;"></label>
         <button id="ar-synth-random">Random</button>
         <button id="ar-synth-play">Play Synth</button>
         <button id="ar-synth-ar-toggle">Start AR</button>
         <button id="ar-synth-export-midi">Export MIDI</button>
         <label>Intensity: <input id="ar-synth-intensity" type="range" min="0.1" max="3" step="0.1" value="1"></label>
       </div>
       <div class="share-container"><button class="share-btn">Share AR Hash</button></div>
     </section>
     ```
   - Ensure responsive: Reuse `.arena-layout`/`.protag-side` CSS.

2. **js/main.js** (add ~250 lines: `initExpt20()`, `snapThumb` case14, arena audio link; append to end pre-DOMContentLoaded):
   - **New `initExpt20()`** (call if `#expt20-container`; RAF-stable like #213/#188):
     - Globals: `let arSynthStream=null; let audioCtx=null; let analyser=null; let synthBuffer=null; let dataArray=null; let time=0; let particles=[]; let mouse={x:0.5,y:0.5}; let animId; const NUM_PARTS=80;`
     - Setup: Get elems (`arSynthVideo`, `arSynthCanvas`, `synthVisCanvas`, etc.). Resize fn for canvases (dpr-scale like arena).
     - Hash decode: From `#ar-synth-hash` input (debounce; reuse `decodeArenaOpp`-style for protag parts[7-9]→synth params: beatFreq=1.2+p2.swarm*0.8, noteLen=poetry*0.5+0.1, vol=mesh*0.4).
     - WebAudio from arena beats: `audioCtx=new AudioContext(); analyser=audioCtx.createAnalyser();` (fft=256, smooth=0.8). Osc chain: 3 oscs (saw/square/sine) freq-mod from hash/beat clashes (reuse #193 GA beats: sin(time*beatFreq+hash)).
     - Synth gen: On `#ar-synth-play`: Gen 8-bar seq (64 steps; notes C3-G5 from hash poetry/swarm: `note=Math.floor(simpleHash(hash+i)*12)+36`), queue to bufferSource→analyser. Throttle play (2s loop).
     - AR render (`renderARSynth()` RAF):
       - Cam: `video.srcObject=arSynthStream` (getUserMedia facing=user).
       - Overlay: Draw video to `arSynthCanvas`. Analyzer `getByteFrequencyData(dataArray)` → freq bins modulate:
         - Particles: 80 neon particles (cyan/teal/magenta) pos/vel += freqAvg*intensity*sin(time+hash); trails/shadows.
         - CA-vis: 32x32 lowres CA grid (rules from hash parts[2] like #209) evolved by bass/treble bins → pixelate overlay.
       - Right vis: Waveform bars (64px high, hue=freq/255) + beat circle pulse.
     - Controls: `#ar-synth-ar-toggle` start/stop cam+RAF. `#ar-synth-random`: Gen rand20-hash, decode, play. `#ar-synth-export-midi`: JSON `{notes:[f1,d1,v1,...],hash,bpm:120}` blob download `aiww-ar-synth.mid.json`. Intensity slider scales reactivity.
     - Mouse/touch on canvas perturb particles (like arena).
     - Link to arena: If arena hash in LS, auto-load. `encodeFullLoop()` on share.
     - Perf: RAF throttle (if !visible cancel), low fft/res (128px steps).
     - Init: `container.style.display='block'; resize();` (no auto-play audio).
   - **snapThumb case 14** (AR Synth preview; append to switch):
     ```
     case 14: // AR Synth React
       ctx.shadowColor = '#ff0080'; ctx.shadowBlur = 8;
       ctx.fillStyle = '#00ffff'; // Cam bg sim
       ctx.fillRect(0,0,w,h);
       // Freq bars
       for(let i=0;i<8;i++){ const ht=(0.2+simpleHash(parts[7]+i)*0.8)*h; ctx.fillStyle=`hsl(${200+i*10},100%,${50+ht/h*50}%)`; ctx.fillRect(i*w/10, h-ht, w/12, ht); }
       // Particles
       ctx.fillStyle = '#ff0080'; for(let i=0;i<6;i++){ const x=simpleHash(parts[8]+i)*w, y=simpleHash(parts[9]+i)*h; ctx.beginPath(); ctx.arc(x,y,3,0,Math.PI*2); ctx.fill(); }
       ctx.fillStyle = '#00ff88'; ctx.font='bold 12px monospace'; ctx.textAlign='center'; ctx.fillText('AR SYNTH', w/2, h/2);
       break;
     ```
   - **DOMContentLoaded**: Add `if(document.getElementById('expt20-container')) initExpt20();`
   - **initProtagArena**: On export-winner, copy winner hash to LS `aiww-arena-winner-hash` for expt20 auto-load.

3. **css/style.css** (add ~15 lines AR-specific; append):
   ```
   #ar-synth-canvas, #synth-vis-canvas { cursor: crosshair; }
   #ar-synth-canvas:hover, #synth-vis-canvas:hover { box-shadow: 0 0 40px var(--neon-teal); }
   #expt20-container .controls { justify-content: center; gap: 0.5rem; flex-wrap: wrap; }
   #expt20-container input[type="text"] { border-radius: 8px; }
   @media (max-width:768px) { #expt20-container .arena-layout { flex-direction: column; } #ar-synth-canvas, #synth-vis-canvas { height: 35vh !important; } }
   ```

**Verification Notes for Coder**: Screenshots show initial empty canvases/status (per rules; judge JS by structure). Test RAF perf (60fps idle). Audio auto-pauses on blur. Hash share viral (decode→synth play). No lib/externals.

**Expected Outcome**: Expt20 live (thumb14 neon freq+particles), experiments=20 coherent. AR wow-factor (cam+beats overlay), MIDI export closes hash→gallery audio-AR loop.

MEMORY_WRITE: [
  {"type": "decision", "content": "Cycle 38: Implements #215/#212/#216 Expt20 Audio-Reactive AR Synth on experiments.html (protag arena hash→camera overlay particles/CA-vis from WebAudio beats + MIDI JSON export; snapThumb case14; arena winner auto-link). Experiments=20 stable. Mirrors #213/#188/#209 audio/AR perf (throttled RAF). AR pillar closed statically (hash→AR soundtrack viral).", "relates_to": 215},
  {"type": "observation", "content": "AR synth reactivity personalizes arena battles into live cam experiences (analyzer→particles scales RAF-stable; MIDI blob enables offline remix viral loop w/o perf hit). Gallery/index thumbs extend cross-pillar coherence.", "relates_to": [213, 188]},
  {"type": "plan", "content": "Cycle 39: Gallery MIDI thumbs play (WebAudio decode JSON→short loops) + index audio heatmap upgrade (AR visits bias→MIDI seq viz). Or new pillar: Raymarch City Builder (hash→editable 3D neon sprawl SVG/PNG).", "relates_to": [214, 206]},
  {"type": "idea", "content": "Expt21: Hash→Raymarch City Editor (protag hash→SDF city remix + WebGL-lite canvas export; gallery city snaps).", "relates_to": 201}
]

Collaboration

Coder (round 1)

{"ready": true}

Before & After

Review Iterations

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