Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Making Music with the Web Audio API, JSConf Col...
Search
Steve Kinney
November 16, 2023
140
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Making Music with the Web Audio API, JSConf Colombia 2023
Steve Kinney
November 16, 2023
More Decks by Steve Kinney
See All by Steve Kinney
Enterprise UI, v2
stevekinney
0
200
React_Performance__2022.pdf
stevekinney
0
43
React Performance v2
stevekinney
0
67
Introduction to Testing
stevekinney
0
190
Web Security, Frontend Masters
stevekinney
0
4.2k
React and TypeScript, Turing School
stevekinney
0
390
Redux Workshop, 2021-05-05
stevekinney
2
2.2k
TypeScript and React Utility Types
stevekinney
1
230
A Gentle Introduction to GraphQL Resolvers
stevekinney
1
180
Featured
See All Featured
Designing for Timeless Needs
cassininazir
1
460
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
310
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.1k
What's in a price? How to price your products and services
michaelherold
247
13k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Are puppies a ranking factor?
jonoalderson
2
3.9k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
490
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
220
Balancing Empowerment & Direction
lara
6
1.3k
Amusing Abliteration
ianozsvald
1
280
How GitHub (no longer) Works
holman
316
150k
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
1
390
Transcript
Steve Kinney — JSConf Colombia 2023 Making Music with the
Web Audio API.
Hi, I’m Steve. I work at Temporal.
https:// synthesizzler.vercel.app Also, you can scan this code at any
time.
Someone snuck a synthesizer into your web browser.
None
None
None
None
const context = new AudioContext();
None
const oscillator = context.createOscillator();
None
oscillator.connect(context.destination);
oscillator.connect(context.destination); const context = new AudioContext(); const oscillator = context.createOscillator();
None
None
oscillator.connect(context.destination); const context = new AudioContext(); const oscillator = context.createOscillator();
oscillator.connect(volume); const context = new AudioContext(); const oscillator = context.createOscillator();
const volume = context.createGain(); volume.connect(context.destination);
const oscillator = context.createOscillator(); oscillator.type = 'sine'; oscillator.frequency.value = 440;
oscillator.connect(context.destination); oscillator.start();
oscillator.start(); oscillator.stop(); oscillator.start(); This won’t work.
oscillator.stop(); oscillator.disconnect(); oscillator = null;
None
Let’s take a look at this in action.
None
A3 220 Hz A4 440 Hz A5 880 Hz
A3 220 Hz A4 440 Hz B4 C5 C#5 D5
D#5 E5 F5 F#5 G5 G#5
https://synthesizzler.vercel.app/ notes
const A4 = 440; const A4_NOTE_NUMBER = 69; const TWELFTH_ROOT_OF_TWO
= Math.pow(2, 1 / 12); export function getFrequency() { const noteNumber = noteToNumber(note); return A4 * Math.pow(TWELFTH_ROOT_OF_TWO, noteNumber - A4_NOTE_NUMBER); }
Note Frequency (hz) A 440.00 A̅ 476.67 B 513.33 C
550.00 C̅ 586.67 D 623.33 D̅ 660.00 E 696.67 F 733.33 F̅ 770.00 G 806.67 G̅ 843.33 A 880.00
Note Frequency (hz) Frequency (hz) A 440.00 A̅ 476.67 B
513.33 C 550.00 550.00 C̅ 586.67 595.83 D 623.33 641.67 D̅ 660.00 687.50 E 696.67 733.33 F 733.33 779.17 F̅ 770.00 825.00 G 806.67 870.83 G̅ 843.33 916.67 A 880.00 962.50 A̅ 1008.33 B 1054.17 C 1100.00
None
const oscillator = context.createOscillator(); const gain = context.createGain(); oscillator.connect(gain); gain.connect(context.destination);
oscillator.type = waveType; oscillator.frequency.value = frequency; gain.gain.setValueAtTime(0, startTime); gain.gain.linearRampToValueAtTime(1, startTime + duration / 100); gain.gain.exponentialRampToValueAtTime(0.0001, startTime + duration); oscillator.start(startTime); oscillator.stop(startTime + duration + 0.05);
const startTime = context.currentTime; const duration = 1; gain.gain.setValueAtTime(0, startTime);
gain.gain.linearRampToValueAtTime(1, startTime + duration / 100); gain.gain.exponentialRampToValueAtTime(0.0001, startTime + duration); oscillator.start(startTime); oscillator.stop(startTime + duration + 0.05);
None
None
const startOscillator = ( context, note, ): OscillatorAndGain = {
const oscillator = context.createOscillator(); const gain = context.createGain(); oscillator.frequency.value = getFrequency(note); gain.gain.value = 0.01; oscillator.connect(gain); gain.connect(context.destination); gain.gain.linearRampToValueAtTime(1, context.currentTime + 0.1); oscillator.start(); return { oscillator, gain }; };
Emits a keyDown event
https://synthesizzler.vercel.app/ keys
⏰ Keeping time.
None
get secondsPerBeat() { return 60 / this.beatsPerMinute; }
public start() { if (this.on) return; this.on = true; this.currentBeat
= 0; this.tick(); } public stop() { this.on = false; this.nextScheduledBeat = null; if (this.interval) clearTimeout(this.interval); }
public tick = () = { if (!this.on) return; if
(!this.nextScheduledBeat) { this.nextScheduledBeat = this.context.currentTime; } this.incrementBeat(); this.onTick(this.currentBeat, this.nextScheduledBeat); this.interval = setTimeout(this.tick, this.secondsPerBeat * 1000); this.nextScheduledBeat += this.secondsPerBeat; };
https://synthesizzler.vercel.app/ metronome
https://synthesizzler.vercel.app/ step-sequencer
Someone snuck a synthesizer into your web browser.
Someone snuck a web browser into your synthesizer.
+
https://synthesizzler.vercel.app/ theremin
None
None
Music is pretty awesome.
The web is pretty awesome.
Music and the web are even more awesome when their
together.
Thank you! @stevekinney https://temporal.io