All Prompts
All Prompts

herosectionuiinteractiveaudioweb-audiotailwindresponsiveskeuomorphic
Generative Audio Hero with Interactive Synth UI
Интерактивный UI генеративного аудио: Hero-секция с синтезатором, слайдерами и настройками. Dark UI на Tailwind.
Prompt
<main
class="flex-none flex flex-col lg:flex-row lg:gap-24 z-10 w-full max-w-7xl mr-auto ml-auto pt-12 pr-6 pb-24 pl-6 relative gap-x-16 gap-y-16 items-center">
<!-- Left Column: Typography -->
<div class="lg:w-1/2 flex flex-col w-full items-start justify-center">
<div class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-zinc-900 border border-zinc-800 mb-8"
style="box-shadow: rgba(255, 255, 255, 0.05) 0px 1px 1px inset;">
<iconify-icon icon="solar:music-notes-linear" class="text-amber-500 text-sm" stroke-width="1.5"></iconify-icon>
<span class="text-xs font-medium tracking-wide text-zinc-400 uppercase">Generative Audio Engine v2.4</span>
</div>
<h1 class="text-5xl md:text-6xl lg:text-7xl font-semibold tracking-tight text-white leading-tight">
Generative Audio for
<span class="text-transparent bg-clip-text bg-gradient-to-br from-zinc-100 to-zinc-500">Infinite Soundscapes.</span>
</h1>
<p class="mt-6 text-lg md:text-xl text-zinc-400 max-w-lg leading-relaxed font-normal">
Generate, sequence, and fine-tune every parameter with precision-driven AI models. Master your sonic environment
through a tactile, zero-latency interface designed for audio professionals.
</p>
<div class="mt-10 flex flex-col sm:flex-row items-center gap-4 w-full sm:w-auto">
<button class="w-full sm:w-auto px-8 py-4 rounded-xl text-sm font-medium text-white flex items-center justify-center gap-2 group relative overflow-hidden transition-all active:scale-95" style="background: linear-gradient(180deg, #27272a 0%, #18181b 100%);
box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.15), 0 4px 12px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0,0,0,0.8);
border: 1px solid #3f3f46;">
<span class="relative z-10">Launch Studio</span>
<iconify-icon icon="solar:arrow-right-linear" class="text-base relative z-10 group-hover:translate-x-1 transition-transform" stroke-width="1.5"></iconify-icon>
<!-- Button inner glow -->
<div class="absolute inset-0 bg-gradient-to-t from-amber-500/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity"></div>
</button>
<button class="sm:w-auto flex transition-all hover:text-white hover:bg-zinc-900/50 text-sm font-medium text-zinc-300 w-full rounded-xl pt-4 pr-8 pb-4 pl-8 gap-x-2 gap-y-2 items-center justify-center" style="border: 1px solid transparent">Listen to Sample</button>
</div>
</div>
<!-- Right Column: Skeuomorphic Hardware Panel -->
<div class="lg:w-1/2 flex lg:justify-end w-full perspective-1000 justify-center">
<div
class="aspect-video min-h-[440px] overflow-hidden flex flex-col bg-gradient-to-b from-[#1e1e22] to-[#0a0a0c] w-full max-w-4xl border-[#2a2a2d] border rounded-[1.25rem] relative shadow-[0_25px_50px_-12px_rgba(0,0,0,1),inset_0_1px_1px_rgba(255,255,255,0.1)]">
<div class="absolute inset-0 pointer-events-none opacity-[0.03]"
style="background-image:url('data:image/svg+xml,%3Csvg viewBox=%220 0 200 200%22 xmlns=%22http://www.w3.org/2000/svg%22%3E%3Cfilter id=%22noiseFilter%22%3E%3CfeTurbulence type=%22fractalNoise%22 baseFrequency=%220.8%22 numaves=%223%22 stitchTiles=%22stitch%22/%3E%3C/filter%3E%3Crect width=%22100%25%22 height=%22100%25%22 filter=%22url(%23noiseFilter)%22/%3E%3C/svg%3E');">
</div>
<header
class="flex z-10 w-full border-[#050505] border-b pt-4 pr-6 pb-4 pl-6 relative shadow-[0_1px_0_rgba(255,255,255,0.03)] items-center justify-between">
<div class="flex items-center gap-4">
<button id="playBtn" type="button" class="w-8 h-8 rounded-full bg-gradient-to-b from-[#2a2a2d] to-[#0a0a0c] border border-black shadow-[0_4px_6px_rgba(0,0,0,0.6),inset_0_1px_1px_rgba(255,255,255,0.15)] flex items-center justify-center active:shadow-[inset_0_3px_6px_rgba(0,0,0,0.8)] active:from-[#0a0a0c] active:to-[#111] transition-all group" aria-label="Play/Pause">
<span class="sr-only">Play/Pause</span>
<svg id="playIcon" width="18" height="18" viewBox="0 0 24 24" class="text-orange-500 drop-shadow-[0_0_8px_rgba(249,115,22,0.8)]" fill="currentColor" aria-hidden="true">
<path d="M8 5v14l11-7z" class=""></path>
</svg>
<svg id="pauseIcon" width="18" height="18" viewBox="0 0 24 24" class="hidden text-orange-500 drop-shadow-[0_0_8px_rgba(249,115,22,0.8)]" fill="currentColor" aria-hidden="true">
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" class=""></path>
</svg>
</button>
<div class="flex flex-col">
<span class="text-neutral-200 text-xs font-semibold tracking-wide drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)]">AI</span>
<span class="text-neutral-500 text-[0.6rem] font-light tracking-widest uppercase drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)]">Music</span>
</div>
</div>
<div
class="flex items-center gap-2.5 px-4 py-2 rounded-full bg-[#050505] shadow-[inset_0_2px_6px_rgba(0,0,0,0.9),0_1px_0_rgba(255,255,255,0.05)] border border-[#000]">
<div
class="w-1.5 h-1.5 rounded-full bg-orange-400 shadow-[0_0_8px_rgba(249,115,22,0.8),inset_0_1px_2px_rgba(255,255,255,0.8)]">
</div>
<span class="text-neutral-300 text-xs font-light tracking-wide">Neural Core Synthesis</span>
<svg width="16" height="16" viewBox="0 0 24 24" class="text-neutral-600 ml-1" fill="none"
stroke="currentColor" stroke-width="1.5" aria-hidden="true">
<path d="M6 9l6 6 6-6" class=""></path>
</svg>
</div>
<div class="flex items-center gap-2">
<button id="undoBtn" type="button" class="w-8 h-8 rounded-full bg-gradient-to-b from-[#2a2a2d] to-[#0a0a0c] border border-black shadow-[0_4px_6px_rgba(0,0,0,0.6)] flex items-center justify-center active:scale-95 transition-all text-neutral-400" aria-label="Undo">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="">
<path d="M9 14l-4-4 4-4" class=""></path>
<path d="M5 10h8a6 6 0 1 1 0 12h-2" class=""></path>
</svg>
</button>
<button id="redoBtn" type="button" class="w-8 h-8 rounded-full bg-gradient-to-b from-[#2a2a2d] to-[#0a0a0c] border border-black shadow-[0_4px_6px_rgba(0,0,0,0.6)] flex items-center justify-center active:scale-95 transition-all text-neutral-400" aria-label="Redo">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="">
<path d="M15 6l4 4-4 4" class=""></path>
<path d="M19 10H11a6 6 0 1 0 0 12h2" class=""></path>
</svg>
</button>
</div>
</header>
<main class="flex-1 grid grid-cols-1 md:grid-cols-3 gap-6 z-10 pr-8 pl-8 relative gap-x-6 gap-y-6 items-center">
<section class="flex justify-center gap-10" aria-label="Faders">
<div class="flex flex-col items-center gap-4">
<span class="text-neutral-500 text-[0.65rem] font-light tracking-widest uppercase">Density</span>
<div class="relative w-10 h-36 flex items-center justify-center">
<input id="density" type="range" min="0" max="100" value="60" class="absolute w-36 h-10 opacity-0 cursor-pointer">
<div
class="relative w-1.5 h-36 bg-[#050505] rounded-full border border-black shadow-[inset_0_3px_6px_rgba(0,0,0,0.9)] flex justify-center">
<div id="densityFill"
class="absolute bottom-1 w-0.5 bg-orange-500 rounded-full shadow-[0_0_10px_rgba(249,115,22,0.8)]"
style="height: 60%;"></div>
<div id="densityThumb"
class="absolute left-1/2 -translate-x-1/2 w-6 h-10 bg-gradient-to-b from-[#333] via-[#111] to-[#050505] rounded-[2px] border border-[#000] shadow-[0_8px_12px_rgba(0,0,0,0.8)] flex flex-col items-center justify-center gap-[2px] pointer-events-none"
style="bottom:calc(60% - 20px);">
<div class="w-3 h-[1px] bg-black/90"></div>
<div class="w-4 h-px bg-white/30"></div>
<div class="w-3 h-[1px] bg-black/90"></div>
</div>
</div>
</div>
<span id="densityVal" class="text-neutral-400 text-[0.65rem] font-light">60</span>
</div>
<div class="flex flex-col items-center gap-4">
<span class="text-neutral-500 text-[0.65rem] font-light tracking-widest uppercase">Drive</span>
<div class="relative w-10 h-36 flex items-center justify-center">
<input id="drive" type="range" min="0" max="100" value="82" class="absolute w-36 h-10 opacity-0 cursor-pointer">
<div
class="relative w-1.5 h-36 bg-[#050505] rounded-full border border-black shadow-[inset_0_3px_6px_rgba(0,0,0,0.9)] flex justify-center">
<div id="driveFill"
class="absolute bottom-1 w-0.5 bg-orange-500 rounded-full shadow-[0_0_10px_rgba(249,115,22,0.8)]"
style="height: 82%;"></div>
<div id="driveThumb"
class="absolute left-1/2 -translate-x-1/2 w-6 h-10 bg-gradient-to-b from-[#333] via-[#111] to-[#050505] rounded-[2px] border border-[#000] shadow-[0_8px_12px_rgba(0,0,0,0.8)] flex flex-col items-center justify-center gap-[2px] pointer-events-none"
style="bottom:calc(82% - 20px);">
<div class="w-3 h-[1px] bg-black/90"></div>
<div class="w-4 h-px bg-white/30"></div>
<div class="w-3 h-[1px] bg-black/90"></div>
</div>
</div>
</div>
<span id="driveVal" class="text-neutral-100 text-[0.65rem] font-semibold">82</span>
</div>
</section>
<section class="flex flex-col items-center justify-center" aria-label="Amount knob">
<div class="relative w-56 h-56 flex items-center justify-center">
<input id="amount" type="range" min="0" max="100" value="74" class="absolute inset-0 opacity-0 cursor-pointer z-20" aria-label="Amount">
<div class="absolute inset-0 rounded-full"
style="background: conic-gradient(from 225deg, #f97316 0deg, #f97316 var(--angle, 220deg), #000 0deg); filter: drop-shadow(rgba(249, 115, 22, 0.35) 0px 0px 10px); --angle: 199.8deg;">
<div
class="absolute inset-[22px] rounded-full bg-gradient-to-b from-[#3a3a3d] to-[#050505] border border-black shadow-[0_15px_30px_rgba(0,0,0,0.9),inset_0_1px_1px_rgba(255,255,255,0.2)] p-1">
<div
class="w-full h-full rounded-full bg-gradient-to-tr from-[#0a0a0c] to-[#1a1a1c] border border-black shadow-[inset_0_10px_20px_rgba(0,0,0,0.9)] flex flex-col items-center justify-center relative">
<div
class="flex flex-col items-center justify-center bg-[#050505] w-24 h-24 rounded-full shadow-[inset_0_4px_8px_rgba(0,0,0,0.8)] border border-black">
<span id="amountVal" class="text-4xl font-light tracking-tight text-white mb-0.5 drop-shadow-[0_0_8px_rgba(255,255,255,0.3)]">74%</span>
<span class="text-[0.55rem] font-light tracking-widest text-neutral-500 uppercase">Amount</span>
</div>
<div
class="absolute top-3 left-1/2 -translate-x-1/2 w-2.5 h-2.5 bg-orange-400 rounded-full border border-black shadow-[0_0_12px_rgba(249,115,22,0.9)]">
</div>
</div>
</div>
</div>
</div>
</section>
<section class="flex flex-col justify-center gap-8 pl-4" aria-label="Modulation sliders">
<div class="flex items-center gap-4 w-full max-w-[180px]" style="--p:35%;">
<span class="w-12 text-[0.6rem] font-light tracking-widest text-neutral-500 text-right uppercase">Wow</span>
<div
class="flex-1 relative h-1 bg-[#050505] rounded-full border border-black shadow-[inset_0_2px_4px_rgba(0,0,0,0.8)] flex items-center cursor-pointer"
style="--fill: var(--p);">
<div class="absolute left-0 h-full bg-neutral-500 rounded-full" style="width: var(--fill);"></div>
<div
class="absolute -translate-x-1/2 w-3.5 h-3.5 bg-gradient-to-b from-[#e5e5e5] to-[#888] rounded-full border border-black shadow-[0_3px_6px_rgba(0,0,0,0.6)]"
style="left:var(--fill);"></div>
<input id="wow" type="range" min="0" max="100" value="35" class="absolute inset-0 opacity-0 cursor-pointer" aria-label="Wow">
</div>
<span id="wowVal" class="w-6 text-[0.65rem] font-light text-neutral-400">35</span>
</div>
<div class="flex items-center gap-4 w-full max-w-[180px]" style="--p:15%;">
<span class="w-12 text-[0.6rem] font-light tracking-widest text-neutral-500 text-right uppercase">Flutter</span>
<div
class="flex-1 relative h-1 bg-[#050505] rounded-full border border-black shadow-[inset_0_2px_4px_rgba(0,0,0,0.8)] flex items-center cursor-pointer"
style="--fill: var(--p);">
<div class="absolute left-0 h-full bg-neutral-500 rounded-full" style="width: var(--fill);"></div>
<div
class="absolute -translate-x-1/2 w-3.5 h-3.5 bg-gradient-to-b from-[#e5e5e5] to-[#888] rounded-full border border-black shadow-[0_3px_6px_rgba(0,0,0,0.6)]"
style="left:var(--fill);"></div>
<input id="flutter" type="range" min="0" max="100" value="15" class="absolute inset-0 opacity-0 cursor-pointer" aria-label="Flutter">
</div>
<span id="flutterVal" class="w-6 text-[0.65rem] font-light text-neutral-400">15</span>
</div>
<div class="flex items-center gap-4 w-full max-w-[180px]" style="--p:8%;">
<span class="w-12 text-[0.6rem] font-light tracking-widest text-neutral-500 text-right uppercase">Noise</span>
<div
class="flex-1 relative h-1 bg-[#050505] rounded-full border border-black shadow-[inset_0_2px_4px_rgba(0,0,0,0.8)] flex items-center cursor-pointer"
style="--fill: var(--p);">
<div class="absolute left-0 h-full bg-neutral-500 rounded-full" style="width: var(--fill);"></div>
<div
class="absolute -translate-x-1/2 w-3.5 h-3.5 bg-gradient-to-b from-[#e5e5e5] to-[#888] rounded-full border border-black shadow-[0_3px_6px_rgba(0,0,0,0.6)]"
style="left:var(--fill);"></div>
<input id="noise" type="range" min="0" max="100" value="8" class="absolute inset-0 opacity-0 cursor-pointer" aria-label="Noise">
</div>
<span id="noiseVal" class="w-6 text-[0.65rem] font-light text-neutral-400">08</span>
</div>
</section>
</main>
<footer
class="relative w-full px-12 pb-6 pt-4 flex items-center justify-between gap-5 z-10 border-t border-[#050505] shadow-[inset_0_1px_0_rgba(255,255,255,0.02)] mt-auto bg-gradient-to-b from-[#111113]/50 to-[#050505]/50"
aria-label="Dry Wet Mix">
<span id="mixLeftLabel" class="text-[0.65rem] tracking-widest uppercase text-orange-400 font-semibold">WET</span>
<div id="mixTrack"
class="flex-1 max-w-xl relative h-1.5 bg-[#050505] rounded-full border border-black shadow-[inset_0_2px_4px_rgba(0,0,0,0.9)] flex items-center cursor-pointer mx-2"
style="--mix:100%;">
<div class="absolute left-1/2 -translate-x-1/2 w-[1px] h-3 bg-[#222] pointer-events-none"></div>
<div class="absolute left-0 h-full bg-orange-500 rounded-full opacity-40" style="width: var(--mix);"></div>
<div
class="absolute -translate-x-1/2 w-5 h-7 bg-gradient-to-b from-[#333] via-[#111] to-[#050505] rounded border border-black shadow-[0_5px_10px_rgba(0,0,0,0.8)] flex items-center justify-center pointer-events-none"
style="left:var(--mix);">
<div class="w-[1.5px] h-3 bg-orange-400 shadow-[0_0_6px_rgba(249,115,22,0.8)] rounded-full"></div>
</div>
<input id="mix" type="range" min="0" max="100" value="100" class="absolute inset-0 opacity-0 cursor-pointer" aria-label="Dry Wet Mix">
</div>
<span id="mixRightLabel" class="text-[0.65rem] font-semibold tracking-widest text-orange-400 uppercase">WET</span>
</footer>
<script>
(function () {
const $ = (id) => document.getElementById(id);
const ui = {
density: $("density"),
drive: $("drive"),
amount: $("amount"),
wow: $("wow"),
flutter: $("flutter"),
noise: $("noise"),
mix: $("mix"),
playBtn: $("playBtn"),
undoBtn: $("undoBtn"),
redoBtn: $("redoBtn"),
playIcon: $("playIcon"),
pauseIcon: $("pauseIcon"),
densityVal: $("densityVal"),
driveVal: $("driveVal"),
amountVal: $("amountVal"),
wowVal: $("wowVal"),
flutterVal: $("flutterVal"),
noiseVal: $("noiseVal"),
densityFill: $("densityFill"),
driveFill: $("driveFill"),
densityThumb: $("densityThumb"),
driveThumb: $("driveThumb"),
mixTrack: $("mixTrack"),
mixLeftLabel: $("mixLeftLabel"),
mixRightLabel: $("mixRightLabel"),
};
function pct(n) {
const v = Math.max(0, Math.min(100, Number(n) || 0));
return v;
}
function setVertical(which, v) {
const p = pct(v);
const fill = which === "density" ? ui.densityFill : ui.driveFill;
const thumb = which === "density" ? ui.densityThumb : ui.driveThumb;
const label = which === "density" ? ui.densityVal : ui.driveVal;
fill.style.height = p + "%";
thumb.style.bottom = `calc(${p}% - 20px)`;
label.textContent = String(p);
if (which === "drive") {
label.classList.remove("text-neutral-400", "font-light");
label.classList.add("text-neutral-100", "font-semibold");
}
}
function setKnob(v) {
const p = pct(v);
const angle = 270 * (p / 100); // 0..270
const ring = ui.amount.closest("section").querySelector("div[style*='conic-gradient']");
ring.style.setProperty("--angle", angle + "deg");
ui.amountVal.textContent = p + "%";
}
function setHSlider(inputEl, valEl, format) {
const p = pct(inputEl.value);
const wrapper = inputEl.parentElement.parentElement;
wrapper.style.setProperty("--p", p + "%");
valEl.textContent = typeof format === "function" ? format(p) : String(p);
}
function setMix(v) {
const p = pct(v);
ui.mixTrack.style.setProperty("--mix", p + "%");
// Label behavior: change left label from DRY to WET after midpoint
if (p > 50) {
ui.mixLeftLabel.textContent = "WET";
ui.mixLeftLabel.classList.remove("text-neutral-500", "font-light");
ui.mixLeftLabel.classList.add("text-orange-400", "font-semibold");
} else {
ui.mixLeftLabel.textContent = "DRY";
ui.mixLeftLabel.classList.remove("text-orange-400", "font-semibold");
ui.mixLeftLabel.classList.add("text-neutral-500", "font-light");
}
}
// Initial paint
setVertical("density", ui.density.value);
setVertical("drive", ui.drive.value);
setKnob(ui.amount.value);
setHSlider(ui.wow, ui.wowVal);
setHSlider(ui.flutter, ui.flutterVal);
setHSlider(ui.noise, ui.noiseVal, (p) => String(p).padStart(2, "0"));
setMix(ui.mix.value);
// WebAudio demo (internal oscillator + noise; no external assets)
const AudioCtx = window.AudioContext || window.webkitAudioContext;
let ctx = null;
let isPlaying = false;
let osc = null, lfo = null, lfoGain = null, amp = null, filter = null, wet = null, dry = null, out = null, noiseSrc = null, noiseGain = null;
function makeNoiseBuffer(context) {
const duration = 1.0;
const buffer = context.createBuffer(1, Math.floor(context.sampleRate * duration), context.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < data.length; i++) data[i] = (Math.random() * 2 - 1) * 0.3;
return buffer;
}
function setupAudio() {
ctx = new AudioCtx();
out = ctx.createGain();
out.gain.value = 0.9;
amp = ctx.createGain();
amp.gain.value = 0.0;
filter = ctx.createBiquadFilter();
filter.type = "lowpass";
filter.frequency.value = 1200;
filter.Q.value = 0.7;
// Simple "wet" path: mild filter + gain; dry path straight
dry = ctx.createGain();
wet = ctx.createGain();
// LFO for wow/flutter
lfo = ctx.createOscillator();
lfo.type = "sine";
lfo.frequency.value = 0.4;
lfoGain = ctx.createGain();
lfoGain.gain.value = 0;
// Main tone
osc = ctx.createOscillator();
osc.type = "sawtooth";
osc.frequency.value = 110;
// Noise
noiseSrc = ctx.createBufferSource();
noiseSrc.buffer = makeNoiseBuffer(ctx);
noiseSrc.loop = true;
noiseGain = ctx.createGain();
noiseGain.gain.value = 0.0;
// Wiring
osc.connect(filter);
filter.connect(amp);
noiseSrc.connect(noiseGain);
noiseGain.connect(amp);
amp.connect(dry);
amp.connect(wet);
dry.connect(out);
wet.connect(out);
out.connect(ctx.destination);
// LFO targets filter frequency and amp gain slightly
lfo.connect(lfoGain);
lfoGain.connect(filter.frequency);
// Start sources
osc.start();
lfo.start();
noiseSrc.start();
updateAudioFromUI();
}
function updateAudioFromUI() {
if (!ctx) return;
const amount = pct(ui.amount.value) / 100; // 0..1
const density = pct(ui.density.value) / 100; // 0..1
const drive = pct(ui.drive.value) / 100; // 0..1
const wow = pct(ui.wow.value) / 100; // 0..1
const flutter = pct(ui.flutter.value) / 100; // 0..1
const noise = pct(ui.noise.value) / 100; // 0..1
const mix = pct(ui.mix.value) / 100; // 0..1
const t = ctx.currentTime;
const smooth = 0.02;
// Amount -> master amplitude
amp.gain.setTargetAtTime(0.06 + amount * 0.55, t, smooth);
// Density -> filter cutoff (more density = brighter)
const cutoff = 300 + density * 5200;
filter.frequency.setTargetAtTime(cutoff, t, smooth);
// Drive -> resonance + mild output boost (pseudo saturation via gain)
filter.Q.setTargetAtTime(0.5 + drive * 8.0, t, smooth);
out.gain.setTargetAtTime(0.7 + drive * 0.35, t, smooth);
// Wow/Flutter -> LFO rate and depth (modulating cutoff)
const lfoRate = 0.2 + wow * 3.0 + flutter * 6.0;
lfo.frequency.setTargetAtTime(lfoRate, t, smooth);
const lfoDepth = (wow * 180) + (flutter * 260); // Hz
lfoGain.gain.setTargetAtTime(lfoDepth, t, smooth);
// Noise -> mix noise in
noiseGain.gain.setTargetAtTime(noise * 0.25, t, smooth);
// Dry/Wet
dry.gain.setTargetAtTime(1.0 - mix, t, smooth);
wet.gain.setTargetAtTime(mix, t, smooth);
// Wet path EQ: darker when more wet
wet.gain.setTargetAtTime(0.85, t, smooth);
}
function play() {
if (!ctx) setupAudio();
ctx.resume();
isPlaying = true;
ui.playIcon.classList.add("hidden");
ui.pauseIcon.classList.remove("hidden");
}
function pause() {
if (!ctx) return;
ctx.suspend();
isPlaying = false;
ui.pauseIcon.classList.add("hidden");
ui.playIcon.classList.remove("hidden");
}
// listeners
ui.density.addEventListener("input", () => { setVertical("density", ui.density.value); updateAudioFromUI(); });
ui.drive.addEventListener("input", () => { setVertical("drive", ui.drive.value); updateAudioFromUI(); });
ui.amount.addEventListener("input", () => { setKnob(ui.amount.value); updateAudioFromUI(); });
ui.wow.addEventListener("input", () => { setHSlider(ui.wow, ui.wowVal); updateAudioFromUI(); });
ui.flutter.addEventListener("input", () => { setHSlider(ui.flutter, ui.flutterVal); updateAudioFromUI(); });
ui.noise.addEventListener("input", () => { setHSlider(ui.noise, ui.noiseVal, (p) => String(p).padStart(2, "0")); updateAudioFromUI(); });
ui.mix.addEventListener("input", () => { setMix(ui.mix.value); updateAudioFromUI(); });
ui.playBtn.addEventListener("click", async () => {
try {
if (!isPlaying) play();
else pause();
} catch (e) {
// no-op
}
});
const defaults = { density: 60, drive: 82, amount: 74, wow: 35, flutter: 15, noise: 8, mix: 100 };
function applyPreset(p) {
ui.density.value = p.density; setVertical("density", p.density);
ui.drive.value = p.drive; setVertical("drive", p.drive);
ui.amount.value = p.amount; setKnob(p.amount);
ui.wow.value = p.wow; setHSlider(ui.wow, ui.wowVal);
ui.flutter.value = p.flutter; setHSlider(ui.flutter, ui.flutterVal);
ui.noise.value = p.noise; setHSlider(ui.noise, ui.noiseVal, (n) => String(n).padStart(2, "0"));
ui.mix.value = p.mix; setMix(p.mix);
updateAudioFromUI();
}
ui.undoBtn.addEventListener("click", () => applyPreset(defaults));
ui.redoBtn.addEventListener("click", () => applyPreset(defaults));
// Ensure knob initial angle is set
setKnob(ui.amount.value);
})();
</script>
</div>
</div>
</main>