THX "Deep Note"
Web Audio API test
HTML
<div class="frame" style="display: none;">
<p>The Audience is Listening</p>
</div>
<button>Play Audio</button>
CSS
body {
background: black;
margin: 0;
}
div.frame {
position: relative;
margin: 2vw;
box-sizing: border-box;
border: 1vw solid darkblue;
width: calc(100vw - 4vw);
height: calc(100vw * 9 / 16);
background: black;
}
p {
color: white;
width: 100%;
text-align: center;
position: absolute;
bottom: calc((100vw * 9 / 16) / 10);
font-size: 5vw;
}
button {
margin: 8px;
}
JavaScript
function rand(min, max) {
return Math.random() * (max - min) + min
}
function mod(a, n) {
return a - n * Math.floor(a / n)
}
function note(x) {
const scale = [0, 2, 4, 5, 7, 9, 10] //D major
return 440 * Math.pow(2, Math.floor(x / 7)) * Math.pow(2, scale[Math.floor(mod(x, 7))] / 12)
}
function playAudio() {
const notes = [12, 10, 7, 3, 0, -4, -7, -11, -14, -18, -25]
const ctx = new AudioContext()
const gain = ctx.createGain()
const gainCurve = Array(1000).fill().map((x, i) => (100 ** (i/1000) - 1) / (100 - 1) * 0.05)
gain.gain.setValueCurveAtTime(gainCurve, ctx.currentTime, 15)
gain.gain.setValueAtTime(0.05, ctx.currentTime+25)
gain.gain.linearRampToValueAtTime(0.00, ctx.currentTime + 30)
const pan = ctx.createStereoPanner()
const panCurve = Array(1000).fill().map((x, i) => -0.7 * Math.cos(Math.PI * 1.5 * (i / 1000)))
pan.pan.setValueAtTime(-0.7, ctx.currentTime)
pan.pan.setValueCurveAtTime(panCurve, ctx.currentTime+1, 8)
gain.connect(pan)
const filter = ctx.createBiquadFilter()
filter.type = "lowshelf"
filter.frequency.setValueAtTime(200, ctx.currentTime)
filter.gain.setValueAtTime(15, ctx.currentTime)
pan.connect(filter)
filter.connect(ctx.destination)
notes.forEach(n => {
for (let i = 0; i < 3; ++i) {
const oc = ctx.createOscillator()
oc.type = "sawtooth"
const wobble = Array(50).fill().map(x => (Math.random() * 2 - 1) * 25)
const wander = [rand(200, 400), rand(200, 400), rand(200, 400)]
oc.detune.setValueCurveAtTime(wobble, ctx.currentTime+15, 15)
oc.frequency.setValueCurveAtTime(wander, ctx.currentTime, 10)
oc.frequency.linearRampToValueAtTime(note(n), ctx.currentTime + 15)
const pan = ctx.createStereoPanner()
pan.pan.value = (Math.random() * 2 - 1) * 1.0
oc.connect(pan)
pan.connect(gain)
oc.start(ctx.currentTime)
...