blip
blip

add ui sounds
without an audio file.

A UI sound does not need an MP3, a CDN, or a loading state. The Web Audio API can synthesize a click, a toggle, or a notification tone directly in the browser, in a few lines, the instant a user interacts.

0kb of audio assets
4web audio nodes
~0.1s per cue
Simple mode: pick the interaction, then shape it by ear. The studio plays every change as you make it.

why generate a sound instead of downloading one?

A downloaded sample is a file: something to host, load, and keep in sync with a design change. A synthesized sound is a function call — there is nothing to fetch before it can play, nothing to re-export when you want it half a tone brighter or ten milliseconds shorter. That is also why it is small: an oscillator and a gain envelope compile to a few hundred bytes of code, not a WAV file.

a downloaded sample

click.mp3

  • 18 kb to host, cache, and version
  • A network request before the first play
  • Fixed pitch and length — retuning means re-editing audio
  • A license to re-check on every rebrand
a synthesized cue

playClick()

  • A few hundred bytes of code, bundled with your app
  • Nothing to fetch — it plays on the gesture that triggered it
  • Half a tone brighter is a number, not a new export
  • Yours, with no attribution to track
the graph

what does that look
like in code?

Four nodes, connected once. Every UI sound on this page — click, toggle, error, notification — is the same four objects with different numbers.
  1. 01

    AudioContext

    The clock and the output device. One per page, created on the first user gesture so autoplay policy never blocks it.

    new AudioContext()
  2. 02

    OscillatorNode

    The pitch. A ramp from a higher frequency to a lower one over 80 ms is what makes a click read as a click rather than a beep.

    ctx.createOscillator()
  3. 03

    GainNode

    The envelope. Without a fast fade in and out, starting and stopping the oscillator produces a step in the waveform you hear as a pop.

    ctx.createGain()
  4. 04

    destination

    The speakers. Connecting the chain here is the whole of the routing — no buffer to decode, no network request to wait on.

    ctx.destination
1.00.500 ms120 msgainattack · 5 msdecay · 115 msno envelope → audible click
The gain envelope is the part people skip. Start and stop an oscillator at full level and the waveform steps — you hear that step as a pop, on top of the sound you meant to play.
click.jsweb audio · by hand
const ctx = new (window.AudioContext || window.webkitAudioContext)();

function playClick() {
  const osc = ctx.createOscillator();
  const gain = ctx.createGain();

  osc.type = 'sine';
  osc.frequency.setValueAtTime(880, ctx.currentTime);
  osc.frequency.exponentialRampToValueAtTime(220, ctx.currentTime + 0.08);

  gain.gain.setValueAtTime(0.0001, ctx.currentTime);
  gain.gain.exponentialRampToValueAtTime(0.6, ctx.currentTime + 0.005);
  gain.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + 0.12);

  osc.connect(gain).connect(ctx.destination);
  osc.start();
  osc.stop(ctx.currentTime + 0.12);
}

document.querySelector('button').addEventListener('click', playClick);
This is the shape of it done by hand, as an illustration of the technique — not what Blip Blip’s own export button produces. The real export is longer, parameter-driven, and bakes in per-play randomized variation, harmonics, and any filter, drive, delay, or reverb stage as literal constants.

The pattern above is the whole idea: an AudioContext, an OscillatorNode for pitch, a GainNode shaped into an attack-and-decay envelope so the click does not pop, and a single connect chain to the speakers. Swap the frequency ramp and envelope shape and the same four objects become a toggle, a soft tap, or an error buzz.

how do I shape it instead of hand-tuning envelope math?

Blip Blip is a browser-based studio built around exactly this graph — oscillator, filter, envelope, and effects — with faders instead of raw Web Audio parameters. Shape a sound by ear, preview it instantly, then export the finished result as ready-to-paste Web Audio JavaScript: the full version, with per-play variation and any filter, drive, delay, or reverb you added, not just the four-line sketch above. The same code runs unchanged in an Electron app’s renderer process. Take the full studio walkthrough to see every mode, or go straight to state-specific sounds like loading, success, and error tones.

export
// blip blip — click
// web audio, generated from the studio

const SAMPLE_RATE = 44100;
const DURATION = 0.11;

export function playClick(ctx) {
  const osc = ctx.createOscillator();
  const gain = ctx.createGain();
  const filter = ctx.createBiquadFilter();

  osc.type = 'triangle';
  osc.frequency.setValueAtTime(1029, ctx.currentTime);
  osc.frequency.exponentialRampToValueAtTime(933, ctx.currentTime + 0.056);

  filter.type = 'lowpass';
  filter.frequency.value = 3300;
  filter.Q.value = 0.7;
  …
one export unlocks web, swift, and kotlin
faq

the useful details.

Do I need to host an audio file for this to work?

No. Everything above is generated at runtime by the Web Audio API — there is no MP3, WAV, or CDN request involved, and nothing to add to your asset pipeline.

Does the Web Audio API work in every browser?

Yes, all current browsers support it. Older Safari versions needed the webkitAudioContext prefix, which is why the example above checks for both.

Can Blip Blip export this for me instead of writing it by hand?

Yes. Design the sound in the studio with Simple or Pro controls, then export it as ready-to-use Web Audio JavaScript. Designing and previewing needs no account; exporting the code does, and the first five exports are free.

ready to make
some noise?

Open Blip Blip and give it a voice.

open the studio