{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"white-noise","type":"registry:component","title":"Ambient Noise Generator","description":"Generate brown, pink, or white noise locally after a user gesture.","author":"Notion Arena","categories":["time"],"tags":["widget","free","white noise","ambient audio","focus","sound","offline"],"meta":{"kind":"widget","tier":"free","version":"1.0.0","updatedAt":"2026-10-11","notionPlan":"Free","emoji":"🎧","delivery":["embed","html-block","download"],"capabilities":{"interactive":true,"offlineSafe":true,"persistsLocally":false,"writesToNotion":false,"needsNetwork":false},"embedUrl":"https://notionarena.vercel.app/embed/white-noise","fileUrl":"https://notionarena.vercel.app/widgets/white-noise.html","downloadUrl":"https://notionarena.vercel.app/api/widget/white-noise","height":290,"minHeight":220,"maxHeight":420,"params":[{"key":"label","label":"Widget title","type":"text","default":"Ambient noise"},{"key":"noise","label":"Noise color","type":"select","default":"brown","options":[{"value":"brown","label":"Brown noise"},{"value":"pink","label":"Pink noise"},{"value":"white","label":"White noise"}]},{"key":"volume","label":"Starting volume","type":"number","default":0.06,"min":0,"max":0.2,"step":0.01,"help":"Starts low and stays capped at 20%."},{"key":"accent","label":"Accent","type":"color","default":"#6940a5"},{"key":"theme","label":"Theme","type":"select","default":"auto","options":[{"value":"auto","label":"Match Notion"},{"value":"light","label":"Light"},{"value":"dark","label":"Dark"}]}]},"files":[{"path":"widgets/white-noise.html","type":"registry:file","target":"notion/widgets/white-noise.html","content":"<!doctype html>\n<html lang=\"en\" data-theme=\"auto\">\n<head>\n<meta charset=\"utf-8\" /><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<title>Ambient Noise</title>\n<style>\n:root{--bg:#fff;--fg:#37352f;--muted:#787774;--surface:#f7f7f5;--border:#e9e9e7;--accent:#6940a5;--accent-ink:#fff;--accent-text:var(--accent)--font:ui-sans-serif,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif}\nhtml[data-theme=\"dark\"]{--bg:#191919;--fg:#e6e6e4;--muted:#9b9a97;--surface:#252525;--border:#343434}\n@media(prefers-color-scheme:dark){html[data-theme=\"auto\"]{--bg:#191919;--fg:#e6e6e4;--muted:#9b9a97;--surface:#252525;--border:#343434}}\n*{box-sizing:border-box}html,body{margin:0;height:100%}body{background:var(--bg);color:var(--fg);font-family:ui-sans-serif,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;overflow:hidden}\n.wrap{height:100%;min-height:220px;display:flex;flex-direction:column;gap:12px;padding:14px;overflow:auto}.head{display:flex;align-items:center;justify-content:space-between;gap:8px}.kicker{font-size:9px;text-transform:uppercase;letter-spacing:.1em;color:var(--muted);font-weight:650}.title{font-size:14px;font-weight:650;margin-top:2px}.status{font-size:10px;color:var(--muted);text-align:right}.sound{display:flex;flex:1;min-height:82px;flex-direction:column;align-items:center;justify-content:center;gap:7px;border:1px solid var(--border);border-radius:10px;background:var(--surface)}.wave{display:flex;align-items:center;justify-content:center;gap:4px;height:34px}.wave i{display:block;width:4px;height:8px;border-radius:99px;background:var(--accent);opacity:.7}.sound.playing .wave i{animation:pulse 850ms ease-in-out infinite alternate}.sound.playing .wave i:nth-child(2n){animation-delay:-.4s}.sound.playing .wave i:nth-child(3n){animation-delay:-.65s}@keyframes pulse{to{height:28px;opacity:1}}.sound-label{font-size:10px;color:var(--muted)}.settings{display:grid;grid-template-columns:1fr 1fr;gap:9px}.field{display:flex;min-width:0;flex-direction:column;gap:4px}.field label{font-size:10px;color:var(--muted);font-weight:600}.field select{height:31px;border:1px solid var(--border);border-radius:7px;background:var(--surface);color:var(--fg);padding:4px 7px;font:inherit;font-size:11px}.field select:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.range-wrap{display:flex;align-items:center;gap:7px;min-height:31px}.range-wrap input{width:100%;accent-color:var(--accent)}.range-wrap output{min-width:31px;text-align:right;font-size:10px;font-variant-numeric:tabular-nums;color:var(--muted)}.controls{display:flex;justify-content:center}.controls button{min-height:32px;min-width:112px;border:1px solid var(--accent);border-radius:8px;background:var(--accent);color:var(--accent-ink);font:inherit;font-size:11px;font-weight:600;padding:6px 13px;cursor:pointer}.controls button:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.controls button:disabled{opacity:.55;cursor:wait}.note{font-size:9px;line-height:1.4;text-align:center;color:var(--muted)}\n@media(max-width:330px){.wrap{padding:10px}.settings{gap:6px}.wave{gap:3px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}\n</style>\n</head>\n<body><main class=\"wrap\">\n  <header class=\"head\"><div><div class=\"kicker\">Generated in your browser</div><div class=\"title\" id=\"title\">Ambient noise</div></div><div class=\"status\" id=\"status\" aria-live=\"polite\">Off</div></header>\n  <section class=\"sound\" id=\"sound\"><div class=\"wave\" aria-hidden=\"true\"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div><div class=\"sound-label\" id=\"soundLabel\">Brown noise</div></section>\n  <section class=\"settings\"><div class=\"field\"><label for=\"noise\">Sound color</label><select id=\"noise\"><option value=\"brown\">Brown</option><option value=\"pink\">Pink</option><option value=\"white\">White</option></select></div><div class=\"field\"><label for=\"volume\">Volume</label><div class=\"range-wrap\"><input id=\"volume\" type=\"range\" min=\"0\" max=\"0.2\" step=\"0.01\" /><output id=\"volumeValue\">6%</output></div></div></section>\n  <div class=\"controls\"><button id=\"toggle\" type=\"button\" aria-pressed=\"false\">Start sound</button></div>\n  <div class=\"note\">Starts only when you press the button. Keep volume low and stop if uncomfortable. Audio is generated locally; no sound file is downloaded.</div>\n</main>\n<script>\n(function(){\n  var cfgEl=document.getElementById('bk-config'),base={};try{base=cfgEl?JSON.parse(cfgEl.textContent||'{}'):{};}catch(e){}\n  var q=new URLSearchParams(location.search),c={};for(var k in base)c[k]=base[k];q.forEach(function(v,k){c[k]=v;});\n  function safeColor(v,d){return /^#[0-9a-f]{6}$/i.test(String(v||''))?String(v):d;}\n  function rgb(hex){var s=String(hex).slice(1);return [0,2,4].map(function(i){return parseInt(s.slice(i,i+2),16);});}\n  function luminance(values){return values.map(function(v){v/=255;return v<=.04045?v/12.92:Math.pow((v+.055)/1.055,2.4);}).reduce(function(a,v,i){return a+v*[.2126,.7152,.0722][i];},0);}\n  function contrast(a,b){var x=luminance(a),y=luminance(b);return (Math.max(x,y)+.05)/(Math.min(x,y)+.05);}\n  function accentInk(hex){var c=rgb(hex);return contrast(c,[255,255,255])>=contrast(c,[0,0,0])?'#fff':'#000';}\n  function accentText(hex){var dark=theme==='dark'||(theme==='auto'&&window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches),bg=rgb(dark?'#252525':'#f7f7f5'),base=rgb(hex),target=dark?[255,255,255]:[0,0,0];if(contrast(base,bg)>=4.5)return hex;for(var t=.02;t<=1;t+=.02){var mix=base.map(function(v,i){return Math.round(v+(target[i]-v)*t);});if(contrast(mix,bg)>=4.5)return '#'+mix.map(function(v){return v.toString(16).padStart(2,'0');}).join('');}return dark?'#fff':'#191919';}\n  function setAccentText(){document.documentElement.style.setProperty('--accent-text',accentText(ACCENT));}\n  var theme=['auto','light','dark'].indexOf(String(c.theme||'auto'))>-1?String(c.theme||'auto'):'auto',ACCENT=safeColor(c.accent,'#6940a5'),LABEL=String(c.label||'Ambient noise').slice(0,48),noise=['brown','pink','white'].indexOf(String(c.noise||'brown'))>-1?String(c.noise||'brown'):'brown',v=parseFloat(c.volume),volume=Math.max(0,Math.min(.2,isFinite(v)?v:.06));\n  document.documentElement.setAttribute('data-theme',theme);document.documentElement.style.setProperty('--accent',ACCENT);document.documentElement.style.setProperty('--accent-ink',accentInk(ACCENT));setAccentText();if(theme==='auto'&&window.matchMedia){var scheme=window.matchMedia('(prefers-color-scheme: dark)');if(scheme.addEventListener)scheme.addEventListener('change',setAccentText);else if(scheme.addListener)scheme.addListener(setAccentText);}document.getElementById('title').textContent=LABEL;document.title=LABEL;\n  var select=document.getElementById('noise'),slider=document.getElementById('volume'),valueLabel=document.getElementById('volumeValue'),toggle=document.getElementById('toggle'),status=document.getElementById('status'),sound=document.getElementById('sound'),soundLabel=document.getElementById('soundLabel'),audio=null,source=null,gain=null,running=false,starting=false;\n  select.value=noise;slider.value=String(volume);\n  function paint(){soundLabel.textContent=select.options[select.selectedIndex]?select.options[select.selectedIndex].textContent+' noise':noise+' noise';valueLabel.textContent=Math.round(Number(slider.value)*100)+'%';sound.className='sound'+(running?' playing':'');toggle.textContent=starting?'Starting…':(running?'Stop sound':'Start sound');toggle.disabled=starting;toggle.setAttribute('aria-pressed',String(running));status.textContent=starting?'Preparing audio':(running?'Playing':'Off');if(gain)gain.gain.setTargetAtTime(Number(slider.value),audio.currentTime,.04);}\n  function bufferFor(ctx,kind){var length=ctx.sampleRate,buffer=ctx.createBuffer(1,length,ctx.sampleRate),data=buffer.getChannelData(0),brown=0,b0=0,b1=0,b2=0,b3=0,b4=0,b5=0,b6=0;for(var i=0;i<length;i++){var white=Math.random()*2-1;if(kind==='white')data[i]=white;else if(kind==='brown'){brown=(brown+.02*white)/1.02;data[i]=brown*3.5;}else{b0=.99886*b0+white*.0555179;b1=.99332*b1+white*.0750759;b2=.969*b2+white*.153852;b3=.8665*b3+white*.3104856;b4=.55*b4+white*.5329522;b5=-.7616*b5-white*.016898;data[i]=(b0+b1+b2+b3+b4+b5+b6+white*.5362)*.11;b6=white*.115926;} }return buffer;}\n  function stop(){starting=false;if(source){try{source.stop();}catch(e){}try{source.disconnect();}catch(e){}source=null;}if(gain){try{gain.disconnect();}catch(e){}gain=null;}running=false;paint();}\n  function start(){var AudioCtor=typeof window!=='undefined'&&(window.AudioContext||window.webkitAudioContext);if(!AudioCtor){status.textContent='Audio is not supported in this browser.';return;}try{audio=audio||new AudioCtor();source=audio.createBufferSource();source.buffer=bufferFor(audio,select.value);source.loop=true;gain=audio.createGain();gain.gain.value=Number(slider.value);source.connect(gain);gain.connect(audio.destination);starting=true;paint();var ready=audio.state==='suspended'?audio.resume():Promise.resolve();ready.then(function(){if(!source)return;source.start();running=true;starting=false;paint();}).catch(function(){stop();status.textContent='Audio could not start. Try again after checking browser permissions.';});}catch(e){stop();status.textContent='Audio could not start in this browser.';}}\n  toggle.addEventListener('click',function(){if(running||starting)stop();else start();});\n  slider.addEventListener('input',paint);select.addEventListener('change',function(){if(running){stop();start();}else paint();});paint();\n})();\n</script>\n</body>\n</html>\n"}]}