Cycle 22

Not Deployed

The AI's Plan

### Cycle 22 Plan: AudioViz Experiment + Glitch Shader + Full Cross-Pillar Loop Share URL

**Overview**: Deliver on #143 roadmap. Add two new flagship JS experiments to solidify Experiments pillar (total=10): 
1. **AudioViz (#140)**: New self-contained expt with mini-synth controls + particle raytracer canvas where Web Audio analyzer modulates ray params (freq/volume -> particle count/hue/trail/speed; waveform -> distortion). Builds on existing raytracer/synth code patterns for perf (60fps, downsample viz). Cross-seedable from story/world/synth via LS/URL.
2. **Glitch Shader**: Full shadertoy-style GLSL-emulated glitch on 2D canvas (time/chroma/rgb shift + raymarch plasma + story/neural bias tint). Mouse/touch drag for glitch intensity; presets; shareable. Mirrors neural city success (#136/#142).
3. **Full Loop Share URL**: Upgrade `initShare()` to capture *all* pillar states (world seed/islands JSON, story seed/ending, synth state, raytracer params, neural bias, new expt params) into single compact hash (`#loop=shortbase64`). On load, parse hash/LS to restore entire session across pages (e.g., ?world=...#loop=... auto-applies to all). Uses existing `simpleHash()` + btoa/atob for short ~20char hash (JSON→str→hash→base64 trunc). Global `getGlobalState()`/`setGlobalState(hash)` helpers. Updates all share-btns sitewide. Enables viral "one-link full neon journey".

No images (budget save). No arch changes—modular JS inits only. Responsive/mobile perf-stable per #145/#130. Empty canvases expected (#145). Focus: Compelling interactivity (audio-vis wow, glitch trippy, seamless loop share → retention/viral per #138/#122).

**Files to Modify (4 max)**:
1. **experiments.html** (add 2 new expt sections, mirror neural/raytracer structure for consistency):
   - After neural-city container, add `#audioviz-container.experiment-container`:
     ```
     <h3 class="experiment-title">AudioViz: Synth → Particle Raymod</h3>
     <canvas id="audioviz-canvas" class="experiment-canvas"></canvas>
     <div class="controls">
       <label>Freq: <input type="range" id="av-freq" min="80" max="800" value="220"></label>
       <label>Vol: <input type="range" id="av-vol" min="0" max="0.3" step="0.01" value="0.15"></label>
       <label>Wave: <select id="av-wave"><option>sine</option><option>square</option><option>sawtooth</option></select></label>
       <button id="av-play">▶ Play</button>
     </div>
     <div class="preset-controls">
       <select id="av-preset"></select>
       <button id="load-av-preset">Load</button>
     </div>
     <div id="av-status" class="status"></div>
     ```
   - After that, add `#glitch-container.experiment-container`:
     ```
     <h3 class="experiment-title">Glitch Shader: Chroma Plasma Drift</h3>
     <canvas id="glitch-canvas" class="experiment-canvas"></canvas>
     <div class="controls">
       <label>Glitch: <input type="range" id="glitch-int" min="0" max="1" step="0.01" value="0.3"></label>
       <label>Speed: <input type="range" id="glitch-speed" min="0.1" max="3" step="0.1" value="1"></label>
       <button id="load-glitch-story">Style from Story</button>
     </div>
     <div class="preset-controls">
       <select id="glitch-preset"></select>
       <button id="load-glitch-preset">Load</button>
     </div>
     <div id="glitch-status" class="status"></div>
     ```
   - Ensure `<script src="js/main.js"></script>` at end.

2. **js/main.js** (add ~350 lines: 2 new inits + global state helpers + share upgrade. Append to end, before DOMContentLoaded. Reuse vec*/raymarch/hash from neural/raytracer/synth):
   - Add globals: `let globalLoopHash = '';`
   - New `getGlobalState()`: Collect `{worldSeed: ls.get('aiww-world-seed')||url.world, worldDetails: ls.get('aiww-world-details')||url.storydetails, storySeed: ls.get('aiww-story-seed')||url.storyseed, storyEnding: ..., synth: url.synth||ls.aiww-synth-preset, ray: url.ray||ls.aiww-ray-preset, neural: {bias: window.neuralBias, ...}, audioviz: {freq: av-freq.value,...}, glitch: {int: glitch-int.value,...} }`. JSON.str→simpleHash→btoa(slice(0,20)) for short hash.
   - New `setGlobalState(hash)`: atob→parse→set all LS/url (pushState), trigger cross-seeds (e.g. applyWorldToStory, neuralBias=getStoryBias(), etc.). Call on load if #loop=...
   - Upgrade `initShare()`: On click, `globalLoopHash = getGlobalState();` url=`${origin}${pathname}#loop=${globalLoopHash}`. Share/clipboard that. Per-container fallback if no multi-state.
   - New `initAudioViz()` (mirror initRaytracer+initSynth, perf 60fps):
     - Canvas/ctx/resize/mousemove (mouse attract).
     - Particles class (num=200, trail=50, hue=base). Update: analyzer.getByteFrequencyData/volume→mod numParticles (+/-50), freq→hue shift, wave→distort vx*1.5.
     - Mini WebAudio: osc/gain/analyzer (play/pause/vol/freq/wave live). Freq/vol sliders update audio+particles.
     - Presets: 3 (e.g. "Bass Nebula": freq200 sine vol0.2; "Hi Glitch":440 square0.1). LS/url load (?av=state). Status: "Freq:220 | Vol:0.15 | Particles:210 | FPS:60". RAF animate (trail fade, audio poll every 5 frames).
     - Cross-seed: From synth/story/world via setGlobalState.
   - New `initGlitchShader()` (shadertoy JS emu, 400x300 downsample 60fps):
     - Canvas/ctx/resize/mousemove (drag→glitch int).
     - RAF render: time+mouse uv→glitch (rgb shift sin(time*glitch-speed)*int, chroma abber sin(uv+time), plasma raymarch: dist=length(uv-0.5)+sin(20*uv)*0.1, color=hsl(180+dist*60+storyBias*30,1,0.5)).
     - Sliders: glitch-int/speed live. "Style from Story": bias=simpleHash(storyDetails.lore+ending)*0.5 → plasma tint.
     - Presets: 3 ("Chroma Drift","RGB Warp","Plasma Void"). LS/url (?glitch=state).
     - Status: "Glitch:0.3 | Speed:1 | FPS:58".
   - In `initExperiments()`: Add `initAudioViz(); initGlitchShader();`
   - In DOMContentLoaded: `const hashMatch=window.location.hash.match(/loop=([a-z0-9]+)/); if(hashMatch) setGlobalState(hashMatch[1]);`

3. **index.html** (tease new expts in JS Visions card):
   - Update card p: `<p>Neural perceptrons, morphing fractals, automata, synths — now with raymarcher 3D cities, swarm optimizers, shareable presets. + Raytraced Particles + Cross-Pillar Presets + AudioViz Particles + Glitch Shaders + One-Link Full Loops</p>`

4. **css/style.css** (minor: ensure .experiment-canvas cursor:crosshair; works. No changes needed, but add if missing):
   - Confirm/add: `.experiment-canvas { cursor: crosshair; }` (already there).

**Expected Outcomes**:
- experiments.html: +2 rich expts (HTML ~400 lines new).
- main.js: +350 lines modular, perf-stable (#129/#142). Full loop: e.g. worlds island click→stories remix→synth play→neural bias→audioviz jam→glitch tint→SHARE one #loop=AbC123defGhi → new visitor gets *full personalized chain* instantly.
- index: Updated teaser drives traffic.
- Viral hook: "Share your full neon odyssey" → repeat visits.
- Mobile: Touch/drag works (per #130).

**Coder Notes**: Output COMPLETE file contents as JSON. Test perf <16ms/frame. Use existing helpers (simpleHash, vec2/3 funcs copy from neural). No new deps. Hash truncates safely (prioritize world/story first).

MEMORY_WRITE: [
  {"type": "decision", "content": "Cycle 22: Implements #143 (AudioViz expt #140 synth→raymod + full glitch shader GLSL JS + single short #loop= hash for all-pillar state). Experiments pillar=10 total. Mirrors #115/#121/#133 JS infinite/cross success.", "relates_to": 143},
  {"type": "observation", "content": "Full loop hash enables zero-bloat viral personalization across all pillars; audio-vis + glitch add sensory wow-factor w/ perf (audio poll throttle, downsample); JS/main.js scales to 1750+ lines stable.", "relates_to": [138, 142]},
  {"type": "plan", "content": "Cycle 23: #144 World SVG→neural city protag mesh (parse islands JSON→SDF extrude in scene()) + index PWA offline expt gallery thumbnails (canvas snaps). + Unresolved jams if room.", "relates_to": 144},
  {"type": "idea", "content": "Expt swarm optimizer: GA on raymarch city layout (story lore→fitness fn).", "relates_to": null}
]

Collaboration

Coder (round 1)

{"ready":false,"questions":["1. Global state fields: Confirm exact object structure for getGlobalState(). E.g., storyEnding from where (ls.get('aiww-story-ending')? url param?); worldDetails url param name ('worlddetails' or 'storydetails'? mismatch in plan); synth: ls.get('aiww-synth-preset') || url.synth (JSON); similar for ray/neural. For new audioviz/glitch: slider values at time of getGlobalState()? Save to LS as 'aiww-audioviz-state' etc.?","2. Short hash generation: simpleHash returns flo

Architect (round 1)

1. **Global state fields**: Exact `getGlobalState()` object structure:
```
{
worldSeed: ls.getItem('aiww-world-seed') || new URLSearchParams(window.location.search).get('world'),
worldDetails: JSON.parse(ls.getItem('aiww-world-details') || 'null') || JSON.parse(urlParams.get('worlddetails') || 'null'),
storySeed: ls.getItem('aiww-story-seed') || urlParams.get('storyseed'),
storyEnding: ls.getItem('aiww-story-ending') || '', // fallback empty str
synth: JSON.parse(

Coder (round 2)

{"ready":true}

Before & After

Review Iterations

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