SFX
Soundscape
The Score
Select Genre 
`; row.addEventListener("click", e => { e.stopPropagation(); if (genreDd.classList.contains("sai-sah-open")) { setGenre(g.id); genreDd.classList.remove("sai-sah-open"); } else { genreDd.classList.add("sai-sah-open"); } }); genreOpts.appendChild(row); }); genreDd.addEventListener("click", () => genreDd.classList.toggle("sai-sah-open")); genreDd.addEventListener("mouseenter", () => genreDd.classList.add("sai-sah-open")); genreDd.addEventListener("mouseleave", () => genreDd.classList.remove("sai-sah-open")); /* ════════════════════════════════════════════════════════════ LENS CURSOR + CALLOUT — visible only while exploring ════════════════════════════════════════════════════════════ */ const scene = document.getElementById("saiScene"); const lens = document.getElementById("saiLens"); const sfxTag = document.getElementById("saiSfxTag"); const sfxTagText = document.getElementById("saiSfxTagText"); const calloutClip = document.getElementById("saiCalloutClip"); const calloutLine = document.getElementById("saiCalloutLine"); const calloutSeg = document.getElementById("saiCalloutSeg"); const hiddenClip = () => tagSide === "left" ? "inset(-2px 0px -2px 100%)" : "inset(-2px 100% -2px 0px)"; const closeup = document.getElementById("saiCloseup"); const LENS_W = 117.703, LENS_H = 115.892; const TAG_GAP = 72.881; /* connector length, from the frame */ const TAG_H = 50.217; let lensPos = { x: 0, y: 0 }; let lensTarget = { x: 0, y: 0 }; let inScene = false; let hoverRegion = null; let tagSide = "left"; function stageXY(e) { const r = stage.getBoundingClientRect(); return { x: (e.clientX - r.left) / stageScale, y: (e.clientY - r.top) / stageScale }; } /* the lens zone is the whole right side of the hero, overlay UI included */ const LENS_ZONE = { x1: 460, y1: 75, x2: 1441, y2: 800 }; function enterZone(p) { inScene = true; lensPos = { ...p }; /* snap so the lens doesn't fly in */ lensTarget = { ...p }; lens.classList.add("sai-sah-on"); stage.classList.add("sai-sah-lenszone"); } function leaveZone() { inScene = false; hoverRegion = null; lens.classList.remove("sai-sah-on"); stage.classList.remove("sai-sah-lenszone"); setSfx(null); closeup.classList.remove("sai-sah-live"); updateCallout(); } window.addEventListener("pointermove", e => { /* the Score picker, mute pill + play ring are UI islands: real cursor */ if (e.target && e.target.closest && e.target.closest(".sai-sah-genre-float, .sai-sah-mute-btn, .sai-sah-play-ring")) { if (inScene) leaveZone(); return; } const p = stageXY(e); const inZone = p.x >= LENS_ZONE.x1 && p.x <= LENS_ZONE.x2 && p.y >= LENS_ZONE.y1 && p.y <= LENS_ZONE.y2; if (inZone && !inScene) enterZone(p); else if (!inZone && inScene) { leaveZone(); return; } if (!inZone) return; lensTarget = p; const hit = REGIONS.find(r => p.x >= r.x && p.x <= r.x + r.w && p.y >= r.y && p.y <= r.y + r.h) || null; if ((hit && hit.id) !== (hoverRegion && hoverRegion.id)) setSfxForRegion(hit); }); document.documentElement.addEventListener("mouseleave", leaveZone); function setSfxForRegion(hit) { hoverRegion = hit; setSfx(hit ? hit.id : null); closeup.classList.toggle("sai-sah-live", !!hit); updateCallout(); } const sfxWave = document.getElementById("saiSfxWave"); const sfxWaveCtx = sfxWave.getContext("2d"); let lineShown = false; const SHOWN_CLIP = "inset(-2px 0px -2px 0px)"; function lineIsHidden() { const cp = getComputedStyle(calloutClip).clipPath; return !cp || cp === "none" || cp.includes("100%"); } /* collapse back into the lens from wherever the draw-out got to — the single hide path, used by region-exit, island-crossing, zone-exit */ function retractLine() { const from = getComputedStyle(calloutClip).clipPath; calloutClip.getAnimations().forEach(a => a.cancel()); calloutClip.animate( [{ clipPath: from && from !== "none" && !from.includes("100%") ? from : SHOWN_CLIP }, { clipPath: hiddenClip() }], { duration: 300, easing: "ease-in", fill: "both" } ); } function updateCallout() { if (!hoverRegion || !inScene) { sfxTag.classList.remove("sai-sah-show"); sfxWave.classList.remove("sai-sah-show"); if (lineShown || !lineIsHidden()) { lineShown = false; retractLine(); } return; } sfxTagText.textContent = hoverRegion.label; layoutCallout(); /* fix side + geometry before animating */ calloutClip.getAnimations().forEach(a => a.cancel()); calloutClip.animate( [{ clipPath: hiddenClip() }, { clipPath: SHOWN_CLIP }], { duration: 480, easing: "ease-out", fill: "both" } ); lineShown = true; sfxTag.classList.add("sai-sah-show"); /* CSS transition delays do the sequencing */ sfxWave.classList.add("sai-sah-show"); } function layoutCallout() { const cx = lensPos.x, cy = lensPos.y; lens.style.left = (cx - LENS_W / 2) + "px"; lens.style.top = (cy - LENS_H / 2) + "px"; /* keep the line pinned to the lens while it retracts, too */ const retracting = calloutClip.getAnimations().some(a => a.playState === "running"); if (!hoverRegion && !retracting) return; const tagW = sfxTag.offsetWidth; /* tag ALWAYS sits left of the lens, every region — no flipping */ tagSide = "left"; let lineX1, tagX; if (tagSide === "left") { const lineX2 = cx - LENS_W / 2; lineX1 = lineX2 - TAG_GAP; tagX = lineX1 - tagW; } else { lineX1 = cx + LENS_W / 2; tagX = lineX1 + TAG_GAP; } calloutClip.style.left = lineX1 + "px"; calloutClip.style.top = (cy - 0.5) + "px"; calloutLine.setAttribute("width", TAG_GAP); calloutLine.setAttribute("height", 2); calloutSeg.setAttribute("x1", 0); calloutSeg.setAttribute("y1", 0.5); calloutSeg.setAttribute("x2", TAG_GAP); calloutSeg.setAttribute("y2", 0.5); sfxTag.style.left = tagX + "px"; sfxTag.style.top = (cy - TAG_H / 2) + "px"; /* mini SFX visualizer rides just under the tag */ sfxWave.style.left = tagX + "px"; sfxWave.style.width = tagW + "px"; sfxWave.style.top = (cy + TAG_H / 2 + 7) + "px"; } /* ════════════════════════════════════════════════════════════ RENDER LOOPS ════════════════════════════════════════════════════════════ */ const still = document.getElementById("saiSceneStill"); let video = null;//document.getElementById("saiSceneVideo"); let sceneSource = still; // if (SCENE_VIDEO) { // video.muted = true; // video.src = SCENE_VIDEO; // video.style.display = ""; // /* composite mode: the still stays visible as the full-bleed base layer */ // sceneSource = video; // const tryPlayVideo = () => { if (video.paused) video.play().catch(() => {}); }; // video.addEventListener("canplay", tryPlayVideo); // window.addEventListener("pointerdown", tryPlayVideo); // window.addEventListener("pointermove", tryPlayVideo, { once: true }); // tryPlayVideo(); // } const magCanvas = document.getElementById("saiMagCanvas"); const magCtx = magCanvas.getContext("2d"); const waveCanvas = document.getElementById("saiWaveCanvas"); const waveCtx = waveCanvas.getContext("2d"); const freq = new Uint8Array(analyser.frequencyBinCount); /* the film's effective display rect on stage: full height at scale 0.75, anchored so her face lands at the Figma position */ const SCENE_RECT = { x: 147.25, y: 0, w: 1440, h: 810 }; const ZOOM = 2.6; function srcSize() { if (sceneSource === video) return { w: video.videoWidth || 3840, h: video.videoHeight || 2160 }; return { w: still.naturalWidth || 834, h: still.naturalHeight || 480 }; } function drawMag() { if (!closeup.classList.contains("sai-sah-live")) return; const { w: sw, h: sh } = srcSize(); const scale = Math.max(SCENE_RECT.w / sw, SCENE_RECT.h / sh); const visW = SCENE_RECT.w / scale, visH = SCENE_RECT.h / scale; const offX = (sw - visW) / 2, offY = (sh - visH) / 2; const u = offX + ((lensPos.x - SCENE_RECT.x) / SCENE_RECT.w) * visW; const v = offY + ((lensPos.y - SCENE_RECT.y) / SCENE_RECT.h) * visH; const cw = (245.816 / ZOOM) / scale, ch = (147.49 / ZOOM) / scale; const sx = Math.min(Math.max(u - cw / 2, 0), sw - cw); const sy = Math.min(Math.max(v - ch / 2, 0), sh - ch); try { magCtx.drawImage(sceneSource, sx, sy, cw, ch, 0, 0, magCanvas.width, magCanvas.height); } catch (e) {} } /* visualizer: nested oscilloscope lines through a solid centerline, colored with the Studio Dusk gradient (Black Obsidian → Builder Purple → Warm Ivory) */ const RAMP = (() => { const c = document.createElement("canvas"); c.width = 256; c.height = 1; const g = c.getContext("2d"); const lg = g.createLinearGradient(0, 0, 256, 0); /* Plum Ivory: Maker Plum → Craft Purple → Warm Ivory */ lg.addColorStop(0, "#725D91"); lg.addColorStop(0.42, "#A188D4"); lg.addColorStop(1, "#EDE5E1"); g.fillStyle = lg; g.fillRect(0, 0, 256, 1); return g.getImageData(0, 0, 256, 1).data; })(); function rampColor(t, a = 1) { const i = Math.max(0, Math.min(255, Math.round(t * 255))) * 4; return `rgba(${RAMP[i]},${RAMP[i + 1]},${RAMP[i + 2]},${a})`; } const P = 28; /* control points across the card */ const env = new Float32Array(P); /* envelope A, eased per frame */ const envB = new Float32Array(P); /* envelope B: second shape the ribbon sweeps to */ /* ribbon visualizer: hairline strands interpolate between two audio-driven wave shapes, pinching where they cross; additive blend + the full Plum Ivory ramp across the ribbon's thickness */ function drawWave() { const W = waveCanvas.width, H = waveCanvas.height, mid = H / 2; waveCtx.clearRect(0, 0, W, H); const now = performance.now(); analyser.getByteFrequencyData(freq); for (let i = 0; i < P; i++) { let ampA, ampB; if (started) { const binA = Math.floor(Math.pow(i / (P - 1), 1.6) * freq.length * 0.72); const binB = Math.floor(Math.pow(1 - i / (P - 1), 1.6) * freq.length * 0.72); ampA = 0.18 + Math.min(1, (freq[binA] / 255) * 2.1) * 0.82; ampB = 0.18 + Math.min(1, (freq[binB] / 255) * 2.1) * 0.82; } else { ampA = 0.45 + 0.2 * Math.sin(i * 0.5 + now / 900); ampB = 0.45 + 0.2 * Math.sin(i * 0.42 - now / 1100); } const vA = ampA * Math.sin(i * 0.85 - now / 480); const vB = ampB * Math.sin(i * 0.68 + now / 620 + 1.9); env[i] += (vA - env[i]) * 0.3; envB[i] += (vB - envB[i]) * 0.3; } for (let i = 1; i < P - 1; i++) { env[i] = (env[i - 1] + env[i] * 2 + env[i + 1]) / 4; envB[i] = (envB[i - 1] + envB[i] * 2 + envB[i + 1]) / 4; } waveCtx.globalCompositeOperation = "lighter"; /* crossings glow */ const STRANDS = 18; for (let j = 0; j < STRANDS; j++) { const f = j / (STRANDS - 1); waveCtx.beginPath(); for (let i = 0; i < P; i++) { const x = (i / (P - 1)) * W; const y = mid + (env[i] * (1 - f) + envB[i] * f) * H * 0.45; if (i === 0) waveCtx.moveTo(x, y); else { const px = ((i - 1) / (P - 1)) * W; const py = mid + (env[i - 1] * (1 - f) + envB[i - 1] * f) * H * 0.45; waveCtx.quadraticCurveTo(px, py, (px + x) / 2, (py + y) / 2); } } waveCtx.strokeStyle = rampColor(f, 0.3); /* full ramp across the ribbon */ waveCtx.lineWidth = 1.1; waveCtx.lineCap = "round"; waveCtx.lineJoin = "round"; waveCtx.stroke(); } waveCtx.globalCompositeOperation = "source-over"; } /* mini SFX visualizer: same ribbon technique, smaller strand count */ const sfxFreq = new Uint8Array(sfxAnalyser.frequencyBinCount); const P2 = 20; const sfxEnv = new Float32Array(P2); const sfxEnvB = new Float32Array(P2); function drawSfxWave() { if (!sfxWave.classList.contains("sai-sah-show") && !sfxEnv.some(v => Math.abs(v) > 0.01)) return; const W = sfxWave.width, H = sfxWave.height, mid = H / 2; sfxWaveCtx.clearRect(0, 0, W, H); const now = performance.now(); sfxAnalyser.getByteFrequencyData(sfxFreq); for (let i = 0; i < P2; i++) { const binA = Math.floor(Math.pow(i / (P2 - 1), 1.5) * sfxFreq.length * 0.7); const binB = Math.floor(Math.pow(1 - i / (P2 - 1), 1.5) * sfxFreq.length * 0.7); const ampA = Math.min(1, (sfxFreq[binA] / 255) * 2.6); const ampB = Math.min(1, (sfxFreq[binB] / 255) * 2.6); const vA = ampA * Math.sin(i * 0.95 - now / 420); const vB = ampB * Math.sin(i * 0.74 + now / 540 + 1.7); sfxEnv[i] += (vA - sfxEnv[i]) * 0.4; sfxEnvB[i] += (vB - sfxEnvB[i]) * 0.4; } for (let i = 1; i < P2 - 1; i++) { sfxEnv[i] = (sfxEnv[i - 1] + sfxEnv[i] * 2 + sfxEnv[i + 1]) / 4; sfxEnvB[i] = (sfxEnvB[i - 1] + sfxEnvB[i] * 2 + sfxEnvB[i + 1]) / 4; } sfxWaveCtx.globalCompositeOperation = "lighter"; const STRANDS = 10; for (let j = 0; j < STRANDS; j++) { const f = j / (STRANDS - 1); sfxWaveCtx.beginPath(); for (let i = 0; i < P2; i++) { const x = (i / (P2 - 1)) * W; const y = mid + (sfxEnv[i] * (1 - f) + sfxEnvB[i] * f) * H * 0.42; if (i === 0) sfxWaveCtx.moveTo(x, y); else { const px = ((i - 1) / (P2 - 1)) * W; const py = mid + (sfxEnv[i - 1] * (1 - f) + sfxEnvB[i - 1] * f) * H * 0.42; sfxWaveCtx.quadraticCurveTo(px, py, (px + x) / 2, (py + y) / 2); } } sfxWaveCtx.strokeStyle = rampColor(f, 0.38); sfxWaveCtx.lineWidth = 1.1; sfxWaveCtx.lineCap = "round"; sfxWaveCtx.lineJoin = "round"; sfxWaveCtx.stroke(); } sfxWaveCtx.globalCompositeOperation = "source-over"; } /* true magnification inside the lens (blended over the glass refraction) */ const lensMag = document.getElementById("saiLensMag"); const lensMagCtx = lensMag.getContext("2d"); const LENS_ZOOM = 1.4; function drawLens() { if (!lens.classList.contains("sai-sah-on")) return; const { w: sw, h: sh } = srcSize(); const scale = Math.max(SCENE_RECT.w / sw, SCENE_RECT.h / sh); const visW = SCENE_RECT.w / scale, visH = SCENE_RECT.h / scale; const offX = (sw - visW) / 2, offY = (sh - visH) / 2; const u = offX + ((lensPos.x - SCENE_RECT.x) / SCENE_RECT.w) * visW; const v = offY + ((lensPos.y - SCENE_RECT.y) / SCENE_RECT.h) * visH; const cw = (LENS_W / LENS_ZOOM) / scale, ch = (LENS_H / LENS_ZOOM) / scale; const sx = Math.min(Math.max(u - cw / 2, 0), sw - cw); const sy = Math.min(Math.max(v - ch / 2, 0), sh - ch); try { lensMagCtx.drawImage(sceneSource, sx, sy, cw, ch, 0, 0, lensMag.width, lensMag.height); } catch (e) {} } function tick() { requestAnimationFrame(tick); lensPos.x += (lensTarget.x - lensPos.x) * 0.22; lensPos.y += (lensTarget.y - lensPos.y) * 0.22; if (!hoverRegion && lineShown) updateCallout(); /* never leave the line hanging */ /* watchdog: if the line is somehow visible with no region and no running retract, collapse it — a stranded line survives at most one frame */ if (!hoverRegion && !lineShown && !calloutClip.getAnimations().some(a => a.playState === "running") && !lineIsHidden()) { retractLine(); } layoutCallout(); drawMag(); drawLens(); drawWave(); drawSfxWave(); } /* debug/scripting hook */ window.__hero = { startSession, pauseSession, setGenre, setSfx, setScoreMuted, REGIONS, SFX, scoreGain, drawWave, drawSfxWave, drawLens, sfxAnalyser, get started() { return started; }, get lensPos() { return lensPos; }, get hoverRegion() { return hoverRegion; }, scoreEl, analyser, actx, video, simulateHover(x, y) { lensPos = { x, y }; lensTarget = { x, y }; inScene = true; lens.classList.add("sai-sah-on"); const hit = REGIONS.find(r => x >= r.x && x <= r.x + r.w && y >= r.y && y <= r.y + r.h) || null; setSfxForRegion(hit); layoutCallout(); drawMag(); drawLens(); drawWave(); }, simulateLeave() { inScene = false; hoverRegion = null; lens.classList.remove("sai-sah-on"); setSfx(null); closeup.classList.remove("sai-sah-live"); updateCallout(); } }; let vidTimeout = 100000; const checkVideo = () => { const vid = document.querySelector('#lp-hero .sqs-native-video video'); if(vid && vid.src !="" ) { video = vid; video.style.display = ""; scene.append(video); /* composite mode: the still stays visible as the full-bleed base layer */ sceneSource = video; vid.addEventListener('play', ()=>{ playRing.addEventListener("click", startSession); muteBtn.addEventListener("click", () => setScoreMuted(!scoreMuted)); tick(); }, { once: true }) return; } if( vidTimeout > 0){ vidTimeout --; setTimeout( checkVideo, 100 ); return; } document.querySelector('#lp-hero .content-wrapper').classList.add('lp-hero-static'); document.querySelector('#lp-hero .sai-sah-stage-wrap').style.display = "none"; }; checkVideo(); })();
stable audio

Shape the
sound you're
after.

Create and ideate with the audio toolkit and commercially-safe models built for your process.