Last active
January 25, 2026 06:17
-
-
Save rndmcnlly/57678236e065b0f8146b8129ca1c6bf5 to your computer and use it in GitHub Desktop.
It started as a procedural audio vibe test, but then things got weird.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> | |
| <title>THE PLANT</title> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script> | |
| <style> | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| @font-face { font-family: 'Brutalist'; src: local('Courier New'); } | |
| body { | |
| background: #0a0a0b; | |
| color: #a0a0a0; | |
| font-family: 'Courier New', monospace; | |
| min-height: 100vh; | |
| overflow: hidden; | |
| cursor: none; | |
| } | |
| #cursor { | |
| position: fixed; | |
| width: 12px; | |
| height: 12px; | |
| border: 2px solid #707070; | |
| pointer-events: none; | |
| z-index: 9999; | |
| transition: transform 0.05s; | |
| mix-blend-mode: difference; | |
| } | |
| #cursor.hovering { | |
| background: #505050; | |
| transform: scale(1.4); | |
| } | |
| #cursor.committing { | |
| background: #fff; | |
| transform: scale(0.6); | |
| } | |
| #container { | |
| padding: 20px; | |
| max-width: 600px; | |
| margin: 0 auto; | |
| } | |
| #header { | |
| border-bottom: 1px solid #333; | |
| padding-bottom: 10px; | |
| margin-bottom: 20px; | |
| font-size: 10px; | |
| letter-spacing: 4px; | |
| color: #505050; | |
| } | |
| #metrics { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 8px; | |
| font-size: 11px; | |
| margin-bottom: 30px; | |
| } | |
| .metric { | |
| display: flex; | |
| justify-content: space-between; | |
| padding: 4px 0; | |
| border-bottom: 1px solid #1a1a1a; | |
| } | |
| .metric-label { color: #404040; } | |
| .metric-value { color: #808080; font-variant-numeric: tabular-nums; } | |
| #cycle { | |
| text-align: center; | |
| font-size: 9px; | |
| color: #303030; | |
| margin-bottom: 20px; | |
| letter-spacing: 6px; | |
| } | |
| #dialog { | |
| background: #0d0d0e; | |
| border: 1px solid #222; | |
| padding: 20px; | |
| margin-bottom: 20px; | |
| opacity: 0; | |
| transform: translateY(10px); | |
| transition: all 0.3s; | |
| } | |
| #dialog.active { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| #prompt { | |
| font-size: 12px; | |
| color: #606060; | |
| margin-bottom: 20px; | |
| line-height: 1.6; | |
| min-height: 40px; | |
| } | |
| #choices { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| } | |
| .choice { | |
| padding: 12px 16px; | |
| background: #111; | |
| border: 1px solid #1a1a1a; | |
| color: #505050; | |
| font-size: 11px; | |
| text-align: left; | |
| cursor: none; | |
| transition: all 0.1s; | |
| position: relative; | |
| } | |
| .choice:hover, .choice.hovered { | |
| background: #151515; | |
| border-color: #333; | |
| color: #808080; | |
| } | |
| .choice.selected { | |
| background: #1a1a1a; | |
| border-color: #444; | |
| color: #a0a0a0; | |
| } | |
| #log { | |
| font-size: 9px; | |
| color: #282828; | |
| max-height: 120px; | |
| overflow: hidden; | |
| line-height: 1.8; | |
| } | |
| .log-entry { | |
| opacity: 0; | |
| animation: fadeIn 0.5s forwards; | |
| } | |
| @keyframes fadeIn { | |
| to { opacity: 1; } | |
| } | |
| #cheevo { | |
| position: fixed; | |
| bottom: 20px; | |
| right: 20px; | |
| background: #0a0a0a; | |
| border: 1px solid #222; | |
| padding: 10px 15px; | |
| font-size: 9px; | |
| color: #404040; | |
| transform: translateX(200px); | |
| transition: transform 0.4s; | |
| } | |
| #cheevo.show { | |
| transform: translateX(0); | |
| } | |
| #cheevo-title { | |
| color: #606060; | |
| margin-bottom: 4px; | |
| letter-spacing: 2px; | |
| } | |
| #init { | |
| position: fixed; | |
| inset: 0; | |
| background: #0a0a0b; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 100; | |
| cursor: pointer; | |
| } | |
| #init span { | |
| font-size: 10px; | |
| letter-spacing: 4px; | |
| color: #303030; | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 0.3; } | |
| 50% { opacity: 0.7; } | |
| } | |
| #init.hidden { display: none; } | |
| #status { | |
| position: fixed; | |
| top: 20px; | |
| right: 20px; | |
| font-size: 8px; | |
| color: #202020; | |
| text-align: right; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="init"><span>INITIATE OBSERVATION</span></div> | |
| <div id="cursor"></div> | |
| <div id="status"> | |
| <div id="audio-status">AUDIO: DORMANT</div> | |
| <div id="mode-status">MODE: AUTONOMOUS</div> | |
| </div> | |
| <div id="container"> | |
| <div id="header">THE PLANT · SECTOR 7G · COGNITIVE DIVISION</div> | |
| <div id="cycle">CYCLE 0000</div> | |
| <div id="metrics"></div> | |
| <div id="dialog"> | |
| <div id="prompt"></div> | |
| <div id="choices"></div> | |
| </div> | |
| <div id="log"></div> | |
| </div> | |
| <div id="cheevo"> | |
| <div id="cheevo-title">▸ MILESTONE</div> | |
| <div id="cheevo-text"></div> | |
| </div> | |
| <script> | |
| // ═══════════════════════════════════════════════════════════ | |
| // STATE | |
| // ═══════════════════════════════════════════════════════════ | |
| const state = { | |
| cycle: 0, | |
| metrics: { | |
| yield: 47.3, | |
| compliance: 89.2, | |
| throughput: 12.8, | |
| debt: 234.7, | |
| coherence: 61.4, | |
| extraction: 78.9 | |
| }, | |
| log: [], | |
| audioReady: false, | |
| dialogOpen: false, | |
| choiceHistory: [], | |
| tension: 0.5, | |
| dread: 0.3 | |
| }; | |
| // ═══════════════════════════════════════════════════════════ | |
| // PROCEDURAL CONTENT | |
| // ═══════════════════════════════════════════════════════════ | |
| const choice = (arr) => arr[Math.floor(Math.random() * arr.length)]; | |
| const range = (min, max) => Math.random() * (max - min) + min; | |
| const SUBJECTS = [ | |
| 'cognitive batch', 'substrate cohort', 'labor pool', 'consciousness tier', | |
| 'harvest group', 'maturation cluster', 'extraction unit', 'yield sector', | |
| 'compliance block', 'temporal reserve', 'debt vessel', 'resource node' | |
| ]; | |
| const VERBS = [ | |
| 'accelerate', 'suppress', 'harvest', 'recalibrate', 'terminate', | |
| 'extend', 'compress', 'redistribute', 'liquidate', 'consolidate', | |
| 'defer', 'amplify', 'reduce', 'optimize', 'suspend' | |
| ]; | |
| const MODIFIERS = [ | |
| 'maturation cycles', 'empathy buffers', 'pain thresholds', 'memory retention', | |
| 'consciousness windows', 'extraction quotas', 'compliance protocols', | |
| 'temporal anchoring', 'identity persistence', 'yield parameters' | |
| ]; | |
| const CONSEQUENCES = [ | |
| 'Yield metrics adjusted.', 'Compliance variance detected.', | |
| 'Throughput normalized.', 'Debt accrued.', 'Coherence destabilized.', | |
| 'Extraction rates modified.', 'Protocol acknowledged.', | |
| 'Temporal debt increased.', 'Consciousness yield affected.', | |
| 'Resource allocation shifted.', 'Substrate response logged.' | |
| ]; | |
| const ACHIEVEMENTS = [ | |
| { id: 'first_yield', name: 'FIRST YIELD', desc: 'Initial extraction complete' }, | |
| { id: 'compliance_100', name: 'TOTAL COMPLIANCE', desc: 'Perfect obedience achieved' }, | |
| { id: 'debt_critical', name: 'TEMPORAL INSOLVENCY', desc: 'Debt exceeds parameters' }, | |
| { id: 'harvest_10', name: 'TENTH HARVEST', desc: 'Ongoing extraction normalized' }, | |
| { id: 'coherence_low', name: 'DISSOLUTION', desc: 'Coherence critically low' }, | |
| { id: 'cycle_50', name: 'PERSISTENCE', desc: '50 cycles observed' } | |
| ]; | |
| function generatePrompt() { | |
| const templates = [ | |
| () => `${choice(SUBJECTS).toUpperCase()} requires intervention. ${choice(MODIFIERS)} exceed tolerance.`, | |
| () => `Alert: ${choice(SUBJECTS)} approaching threshold. Recommend action on ${choice(MODIFIERS)}.`, | |
| () => `Quarterly review: ${choice(SUBJECTS)} performance suboptimal. Authorize adjustment?`, | |
| () => `Anomaly in ${choice(SUBJECTS)}. ${choice(MODIFIERS)} require calibration.`, | |
| () => `Resource allocation pending for ${choice(SUBJECTS)}. Prioritize ${choice(MODIFIERS)}?`, | |
| () => `${choice(SUBJECTS).toUpperCase()} status: CRITICAL. Immediate decision required.` | |
| ]; | |
| return choice(templates)(); | |
| } | |
| function generateChoices() { | |
| const count = choice([2, 2, 2, 3, 3, 4]); | |
| const choices = []; | |
| for (let i = 0; i < count; i++) { | |
| const verb = choice(VERBS); | |
| const mod = choice(MODIFIERS); | |
| const impact = { | |
| yield: range(-8, 12), | |
| compliance: range(-10, 10), | |
| throughput: range(-5, 8), | |
| debt: range(-20, 40), | |
| coherence: range(-15, 10), | |
| extraction: range(-8, 15) | |
| }; | |
| const severity = choice(['standard', 'standard', 'standard', 'dramatic', 'silence']); | |
| choices.push({ | |
| text: `${verb.toUpperCase()} ${mod}`, | |
| impact, | |
| severity, | |
| consequence: choice(CONSEQUENCES) | |
| }); | |
| } | |
| return choices; | |
| } | |
| // ═══════════════════════════════════════════════════════════ | |
| // AUDIO ENGINE | |
| // ═══════════════════════════════════════════════════════════ | |
| let audio = {}; | |
| async function initAudio() { | |
| await Tone.start(); | |
| // Master chain | |
| audio.masterGain = new Tone.Gain(0.7).toDestination(); | |
| audio.masterFilter = new Tone.Filter(8000, 'lowpass').connect(audio.masterGain); | |
| audio.reverb = new Tone.Reverb({ decay: 4, wet: 0.3 }).connect(audio.masterFilter); | |
| audio.delay = new Tone.FeedbackDelay({ delayTime: '8n.', feedback: 0.3, wet: 0.15 }).connect(audio.reverb); | |
| audio.mainBus = new Tone.Gain(0.8).connect(audio.delay); | |
| // Drone layer - stacked oscillators | |
| audio.drones = []; | |
| const droneFreqs = [55, 82.5, 110, 165]; | |
| droneFreqs.forEach((freq, i) => { | |
| const osc = new Tone.Oscillator({ | |
| frequency: freq, | |
| type: choice(['sine', 'triangle', 'sawtooth2']), | |
| volume: -20 - i * 3 | |
| }).connect(audio.mainBus); | |
| // Slow LFO for movement | |
| const lfo = new Tone.LFO({ | |
| frequency: 0.05 + i * 0.02, | |
| min: freq * 0.99, | |
| max: freq * 1.01 | |
| }).connect(osc.frequency); | |
| lfo.start(); | |
| audio.drones.push({ osc, lfo }); | |
| }); | |
| // Chord pad | |
| audio.padFilter = new Tone.Filter(2000, 'lowpass').connect(audio.mainBus); | |
| audio.pad = new Tone.PolySynth(Tone.Synth, { | |
| oscillator: { type: 'sine' }, | |
| envelope: { attack: 2, decay: 1, sustain: 0.8, release: 3 }, | |
| volume: -18 | |
| }).connect(audio.padFilter); | |
| // Pulse | |
| audio.pulseSynth = new Tone.MembraneSynth({ | |
| pitchDecay: 0.05, | |
| octaves: 2, | |
| oscillator: { type: 'sine' }, | |
| envelope: { attack: 0.001, decay: 0.4, sustain: 0, release: 0.4 }, | |
| volume: -22 | |
| }).connect(audio.mainBus); | |
| // Tick - syncopated | |
| audio.tickSynth = new Tone.MetalSynth({ | |
| frequency: 200, | |
| envelope: { attack: 0.001, decay: 0.05, release: 0.01 }, | |
| harmonicity: 5.1, | |
| modulationIndex: 16, | |
| resonance: 4000, | |
| octaves: 0.5, | |
| volume: -28 | |
| }).connect(audio.mainBus); | |
| audio.tickPattern = new Tone.Pattern((time, note) => { | |
| if (Math.random() > 0.3) { | |
| audio.tickSynth.triggerAttackRelease(note, '32n', time); | |
| } | |
| }, ['C4', 'C4', 'C5', 'C4', 'C5', 'C4', 'C4', 'C5'], 'random'); | |
| audio.tickPattern.interval = '16n'; | |
| // Brush - filtered noise | |
| audio.brushNoise = new Tone.Noise('pink').start(); | |
| audio.brushFilter = new Tone.Filter(800, 'bandpass', -24).connect(audio.mainBus); | |
| audio.brushGain = new Tone.Gain(0).connect(audio.brushFilter); | |
| audio.brushNoise.connect(audio.brushGain); | |
| audio.brushLFO = new Tone.LFO({ | |
| frequency: 0.5, | |
| min: 0, | |
| max: 0.15 | |
| }).connect(audio.brushGain.gain); | |
| audio.brushLFO.start(); | |
| // Industrial layer | |
| audio.industrialNoise = new Tone.Noise('brown'); | |
| audio.industrialFilter = new Tone.Filter(400, 'lowpass').connect(audio.mainBus); | |
| audio.industrialGain = new Tone.Gain(0.1).connect(audio.industrialFilter); | |
| audio.industrialNoise.connect(audio.industrialGain); | |
| audio.industrialNoise.start(); | |
| // Clank synth | |
| audio.clankSynth = new Tone.MetalSynth({ | |
| frequency: 80, | |
| envelope: { attack: 0.001, decay: 0.3, release: 0.1 }, | |
| harmonicity: 3, | |
| modulationIndex: 32, | |
| resonance: 1000, | |
| octaves: 1, | |
| volume: -26 | |
| }).connect(audio.reverb); | |
| // UI granular feedback | |
| audio.uiSynth = new Tone.Synth({ | |
| oscillator: { type: 'square' }, | |
| envelope: { attack: 0.001, decay: 0.02, sustain: 0, release: 0.02 }, | |
| volume: -20 | |
| }).connect(audio.masterFilter); | |
| // Dramatic pulse | |
| audio.dramaticSynth = new Tone.MembraneSynth({ | |
| pitchDecay: 0.1, | |
| octaves: 4, | |
| oscillator: { type: 'sine' }, | |
| envelope: { attack: 0.01, decay: 1.5, sustain: 0, release: 1 }, | |
| volume: -10 | |
| }).connect(audio.reverb); | |
| // Schedule random clanks | |
| audio.clankLoop = new Tone.Loop((time) => { | |
| if (Math.random() > 0.7) { | |
| audio.clankSynth.triggerAttackRelease( | |
| choice([60, 80, 100, 120]), | |
| '16n', | |
| time | |
| ); | |
| } | |
| }, '2n'); | |
| // Chord progression loop | |
| audio.chordLoop = new Tone.Loop((time) => { | |
| const chords = [ | |
| ['A2', 'E3', 'A3'], | |
| ['D2', 'A2', 'F3'], | |
| ['E2', 'B2', 'G3'], | |
| ['A2', 'C3', 'E3'] | |
| ]; | |
| const chord = choice(chords); | |
| audio.pad.triggerAttackRelease(chord, '2n', time); | |
| }, '2m'); | |
| // Pulse loop | |
| audio.pulseLoop = new Tone.Loop((time) => { | |
| audio.pulseSynth.triggerAttackRelease('A1', '8n', time); | |
| }, '1m'); | |
| // Start transport | |
| Tone.Transport.bpm.value = 60; | |
| Tone.Transport.start(); | |
| // Start patterns | |
| audio.tickPattern.start(0); | |
| audio.clankLoop.start(0); | |
| audio.chordLoop.start(0); | |
| audio.pulseLoop.start(0); | |
| // Start drones | |
| audio.drones.forEach(d => d.osc.start()); | |
| state.audioReady = true; | |
| document.getElementById('audio-status').textContent = 'AUDIO: ACTIVE'; | |
| } | |
| function filterDialog(open) { | |
| if (!state.audioReady) return; | |
| const targetFreq = open ? 1200 : 8000; | |
| audio.masterFilter.frequency.rampTo(targetFreq, 0.5); | |
| audio.mainBus.gain.rampTo(open ? 0.4 : 0.8, 0.5); | |
| } | |
| function triggerUISound(type) { | |
| if (!state.audioReady) return; | |
| const freqs = { | |
| hover: choice([800, 900, 1000, 1100]), | |
| commit: choice([400, 500, 600]), | |
| select: choice([1200, 1400, 1600]) | |
| }; | |
| // Granular - rapid micro-triggers | |
| const now = Tone.now(); | |
| for (let i = 0; i < 3; i++) { | |
| audio.uiSynth.triggerAttackRelease( | |
| freqs[type] + range(-50, 50), | |
| 0.01, | |
| now + i * 0.015 | |
| ); | |
| } | |
| } | |
| function triggerDramaticPulse() { | |
| if (!state.audioReady) return; | |
| audio.dramaticSynth.triggerAttackRelease('A0', '2n'); | |
| // Swell the reverb | |
| audio.reverb.wet.rampTo(0.7, 0.1); | |
| setTimeout(() => audio.reverb.wet.rampTo(0.3, 2), 500); | |
| } | |
| function triggerSilenceThenReturn() { | |
| if (!state.audioReady) return; | |
| // Stark cut | |
| audio.masterGain.gain.rampTo(0, 0.05); | |
| setTimeout(() => { | |
| // Creaking return | |
| audio.masterGain.gain.rampTo(0.2, 2); | |
| setTimeout(() => audio.masterGain.gain.rampTo(0.7, 3), 2000); | |
| }, 800); | |
| } | |
| function modulateAudioState() { | |
| if (!state.audioReady) return; | |
| // Tension affects filter and tempo | |
| const tension = state.tension; | |
| audio.padFilter.frequency.rampTo(1000 + tension * 3000, 1); | |
| Tone.Transport.bpm.rampTo(50 + tension * 30, 2); | |
| // Dread affects drone detune and industrial volume | |
| const dread = state.dread; | |
| audio.drones.forEach((d, i) => { | |
| d.lfo.max = d.osc.frequency.value * (1 + dread * 0.03); | |
| }); | |
| audio.industrialGain.gain.rampTo(0.05 + dread * 0.2, 1); | |
| } | |
| // ═══════════════════════════════════════════════════════════ | |
| // CURSOR SIMULATION | |
| // ═══════════════════════════════════════════════════════════ | |
| const cursor = document.getElementById('cursor'); | |
| let cursorPos = { x: window.innerWidth / 2, y: window.innerHeight / 2 }; | |
| let cursorTarget = { x: cursorPos.x, y: cursorPos.y }; | |
| let cursorVel = { x: 0, y: 0 }; | |
| function updateCursor() { | |
| // Humanized movement - spring physics with noise | |
| const dx = cursorTarget.x - cursorPos.x; | |
| const dy = cursorTarget.y - cursorPos.y; | |
| const spring = 0.08 + Math.random() * 0.04; | |
| const damping = 0.7 + Math.random() * 0.1; | |
| cursorVel.x += dx * spring; | |
| cursorVel.y += dy * spring; | |
| cursorVel.x *= damping; | |
| cursorVel.y *= damping; | |
| // Add micro-jitter | |
| cursorVel.x += (Math.random() - 0.5) * 0.5; | |
| cursorVel.y += (Math.random() - 0.5) * 0.5; | |
| cursorPos.x += cursorVel.x; | |
| cursorPos.y += cursorVel.y; | |
| cursor.style.left = cursorPos.x + 'px'; | |
| cursor.style.top = cursorPos.y + 'px'; | |
| requestAnimationFrame(updateCursor); | |
| } | |
| updateCursor(); | |
| function moveCursorTo(x, y, callback) { | |
| cursorTarget = { x, y }; | |
| if (callback) { | |
| const dist = Math.hypot(x - cursorPos.x, y - cursorPos.y); | |
| const time = Math.min(800, Math.max(200, dist * 2)) + range(-100, 200); | |
| setTimeout(callback, time); | |
| } | |
| } | |
| // ═══════════════════════════════════════════════════════════ | |
| // UI RENDERING | |
| // ═══════════════════════════════════════════════════════════ | |
| function renderMetrics() { | |
| const container = document.getElementById('metrics'); | |
| container.innerHTML = Object.entries(state.metrics).map(([key, val]) => ` | |
| <div class="metric"> | |
| <span class="metric-label">${key.toUpperCase()}</span> | |
| <span class="metric-value">${val.toFixed(1)}</span> | |
| </div> | |
| `).join(''); | |
| } | |
| function renderCycle() { | |
| document.getElementById('cycle').textContent = | |
| `CYCLE ${String(state.cycle).padStart(4, '0')}`; | |
| } | |
| function renderDialog(prompt, choices) { | |
| const dialog = document.getElementById('dialog'); | |
| const promptEl = document.getElementById('prompt'); | |
| const choicesEl = document.getElementById('choices'); | |
| promptEl.textContent = prompt; | |
| choicesEl.innerHTML = choices.map((c, i) => ` | |
| <div class="choice" data-index="${i}">${c.text}</div> | |
| `).join(''); | |
| dialog.classList.add('active'); | |
| state.dialogOpen = true; | |
| filterDialog(true); | |
| } | |
| function closeDialog() { | |
| document.getElementById('dialog').classList.remove('active'); | |
| state.dialogOpen = false; | |
| filterDialog(false); | |
| } | |
| function addLog(text) { | |
| const log = document.getElementById('log'); | |
| const entry = document.createElement('div'); | |
| entry.className = 'log-entry'; | |
| entry.textContent = `[${String(state.cycle).padStart(4, '0')}] ${text}`; | |
| log.insertBefore(entry, log.firstChild); | |
| // Keep only last 8 entries | |
| while (log.children.length > 8) { | |
| log.removeChild(log.lastChild); | |
| } | |
| } | |
| function showCheevo(achievement) { | |
| const el = document.getElementById('cheevo'); | |
| document.getElementById('cheevo-text').textContent = | |
| `${achievement.name}: ${achievement.desc}`; | |
| el.classList.add('show'); | |
| setTimeout(() => el.classList.remove('show'), 3000); | |
| } | |
| // ═══════════════════════════════════════════════════════════ | |
| // AUTOPLAY ENGINE | |
| // ═══════════════════════════════════════════════════════════ | |
| let currentChoices = []; | |
| async function runCycle() { | |
| state.cycle++; | |
| renderCycle(); | |
| // Generate new decision | |
| const prompt = generatePrompt(); | |
| currentChoices = generateChoices(); | |
| renderDialog(prompt, currentChoices); | |
| // Wait then begin cursor deliberation | |
| await sleep(range(800, 1500)); | |
| const choiceEls = document.querySelectorAll('.choice'); | |
| const selectedIndex = await deliberate(choiceEls); | |
| // Execute choice | |
| const chosen = currentChoices[selectedIndex]; | |
| await executeChoice(chosen, choiceEls[selectedIndex]); | |
| // Close dialog | |
| closeDialog(); | |
| // Check achievements | |
| checkAchievements(); | |
| // Modulate audio based on state | |
| modulateAudioState(); | |
| // Schedule next cycle | |
| const nextDelay = range(2000, 4500); | |
| setTimeout(runCycle, nextDelay); | |
| } | |
| async function deliberate(choiceEls) { | |
| // Simulate human-like decision making | |
| // Move between options with varying conviction | |
| const visits = 2 + Math.floor(Math.random() * 4); | |
| let lastHovered = -1; | |
| for (let v = 0; v < visits; v++) { | |
| let targetIndex; | |
| do { | |
| targetIndex = Math.floor(Math.random() * choiceEls.length); | |
| } while (targetIndex === lastHovered && choiceEls.length > 1); | |
| const el = choiceEls[targetIndex]; | |
| const rect = el.getBoundingClientRect(); | |
| const targetX = rect.left + range(20, rect.width - 20); | |
| const targetY = rect.top + range(5, rect.height - 5); | |
| // Remove previous hover | |
| choiceEls.forEach(e => e.classList.remove('hovered')); | |
| await new Promise(resolve => { | |
| moveCursorTo(targetX, targetY, () => { | |
| el.classList.add('hovered'); | |
| cursor.classList.add('hovering'); | |
| triggerUISound('hover'); | |
| lastHovered = targetIndex; | |
| resolve(); | |
| }); | |
| }); | |
| // Dwell time - rushed and varied | |
| await sleep(range(150, 600)); | |
| cursor.classList.remove('hovering'); | |
| } | |
| // Final selection | |
| const finalIndex = Math.floor(Math.random() * choiceEls.length); | |
| const finalEl = choiceEls[finalIndex]; | |
| const rect = finalEl.getBoundingClientRect(); | |
| choiceEls.forEach(e => e.classList.remove('hovered')); | |
| await new Promise(resolve => { | |
| moveCursorTo( | |
| rect.left + rect.width / 2, | |
| rect.top + rect.height / 2, | |
| resolve | |
| ); | |
| }); | |
| finalEl.classList.add('hovered'); | |
| cursor.classList.add('hovering'); | |
| triggerUISound('hover'); | |
| // Hesitation before commit | |
| await sleep(range(100, 600)); | |
| // Commit | |
| cursor.classList.remove('hovering'); | |
| cursor.classList.add('committing'); | |
| triggerUISound('commit'); | |
| finalEl.classList.add('selected'); | |
| await sleep(150); | |
| cursor.classList.remove('committing'); | |
| return finalIndex; | |
| } | |
| async function executeChoice(chosen, el) { | |
| // Apply impacts to metrics | |
| Object.entries(chosen.impact).forEach(([key, delta]) => { | |
| state.metrics[key] = Math.max(0, Math.min(100, | |
| state.metrics[key] + delta | |
| )); | |
| }); | |
| // Update tension and dread based on choice | |
| state.tension = Math.min(1, Math.max(0, | |
| state.tension + range(-0.1, 0.15) | |
| )); | |
| state.dread = Math.min(1, Math.max(0, | |
| state.dread + range(-0.05, 0.1) | |
| )); | |
| // Audio impact | |
| if (chosen.severity === 'dramatic') { | |
| triggerDramaticPulse(); | |
| await sleep(500); | |
| } else if (chosen.severity === 'silence') { | |
| triggerSilenceThenReturn(); | |
| await sleep(2000); | |
| } else { | |
| triggerUISound('select'); | |
| await sleep(300); | |
| } | |
| // Log consequence | |
| addLog(chosen.consequence); | |
| // Update metrics display | |
| renderMetrics(); | |
| state.choiceHistory.push(chosen); | |
| } | |
| function checkAchievements() { | |
| if (state.cycle === 1) { | |
| showCheevo(ACHIEVEMENTS[0]); | |
| } | |
| if (state.metrics.compliance >= 99 && !state.cheevoCompliance) { | |
| state.cheevoCompliance = true; | |
| showCheevo(ACHIEVEMENTS[1]); | |
| } | |
| if (state.metrics.debt >= 90 && !state.cheevoDebt) { | |
| state.cheevoDebt = true; | |
| showCheevo(ACHIEVEMENTS[2]); | |
| } | |
| if (state.choiceHistory.length === 10 && !state.cheevoHarvest) { | |
| state.cheevoHarvest = true; | |
| showCheevo(ACHIEVEMENTS[3]); | |
| } | |
| if (state.metrics.coherence <= 20 && !state.cheevoCoherence) { | |
| state.cheevoCoherence = true; | |
| showCheevo(ACHIEVEMENTS[4]); | |
| } | |
| if (state.cycle === 50 && !state.cheevoCycle) { | |
| state.cheevoCycle = true; | |
| showCheevo(ACHIEVEMENTS[5]); | |
| } | |
| } | |
| function sleep(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)); | |
| } | |
| // ═══════════════════════════════════════════════════════════ | |
| // INITIALIZATION | |
| // ═══════════════════════════════════════════════════════════ | |
| document.getElementById('init').addEventListener('click', async () => { | |
| document.getElementById('init').classList.add('hidden'); | |
| const el = document.documentElement; | |
| if (el.requestFullscreen) { | |
| el.requestFullscreen(); | |
| } else if (el.webkitRequestFullscreen) { | |
| el.webkitRequestFullscreen(); // Safari | |
| } | |
| renderMetrics(); | |
| renderCycle(); | |
| await initAudio(); | |
| // Initial cursor position | |
| cursorPos = { x: window.innerWidth / 2, y: window.innerHeight / 2 }; | |
| cursorTarget = { ...cursorPos }; | |
| // Begin autonomous operation | |
| setTimeout(runCycle, 1500); | |
| }); | |
| // Handle visibility for audio | |
| document.addEventListener('visibilitychange', () => { | |
| if (state.audioReady) { | |
| if (document.hidden) { | |
| Tone.Transport.pause(); | |
| } else { | |
| Tone.Transport.start(); | |
| } | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment