open source  ·  zero audio files  ·  zero dependencies

Interaction
sounds
for the web.

No audio files. No config. No setup.
Pure Web Audio API synthesis at runtime.
32 sounds. One import.

npm i sonance
32sounds
0audio files
0dependencies
v2chain api

Your button clicks in silence.
Your form submits in silence.
Your delete confirmation —
the one that wipes everything —
in silence.

playground

Hear it.
Then decide.

All 32 sounds. Generated in your browser at runtime. No files fetched. Just math turned into sound.

// first click wakes the audio engine. browser policy, not ours.

Apple has a sound team.
Slack has a sound team.
You have one import.

sound library

Every sound,
a reason.

Not samples. Not presets. Each one is synthesized — specific frequency, envelope, and character designed for a specific moment in your UI.

Sound reduces cognitive load.
Users stop watching the screen.
They start trusting the product.

usage

One import.
Anywhere.

Not a React library. Not a Vue plugin. A plain JavaScript object. Works wherever JS runs.

tsx
import { sonance } from "sonance"

export function SaveButton() {
  async function handle() {
    try {
      await save()
      sonance.play("success")
    } catch {
      sonance.play("error")
    }
  }
  return <button onClick={handle}>Save</button>
}
js
import { sonance } from "sonance"

document.querySelector("#save")
  .addEventListener("click", async () => {
    sonance.play("click")
    await save()
    sonance.play("saved")
  })
vue
<script setup>
import { sonance } from "sonance"
</script>
<template>
  <button @click="sonance.play('confirm')">
    Save
  </button>
</template>
svelte
<script>
  import { sonance } from "sonance"
</script>
<button on:click={() => sonance.play("confirm")}>
  Save
</button>
sequences

Chain API.
For dramatic people.

Every play() returns a handle. Which means you can do things that are either genuinely useful or deeply unnecessary. Both are valid.

api

The whole API.

We're not hiding anything. This is genuinely it.

.play(name, opts?)
plays a sound. returns a chainable handle.
.play().then(name, ms)
plays another sound after a delay.
.play().repeat(n, ms)
repeats this sound n times with gap between.
.play().fade(gain, ms)
fades to target gain. fade(0) auto-stops.
.play().pitch(semitones)
shifts pitch. +12 = octave up, -12 = down.
.play().speed(factor)
scales playback speed. 2.0 = double speed.
.play().stop()
stops this sound immediately.
.stop()
stops all currently playing sounds.
.volume(0–1)
global volume. ramps over 80ms to avoid clicks.
.register(def)
register a custom sound or override a built-in.
ts
import { sonance } from "sonance"

// options: gain, pitch (semitones), speed
sonance.play("confirm", { pitch: +4, gain: 0.8 })

// chain
sonance.play("confirm")
  .then("success", 300)

// custom sound
sonance.register({
  id: "myPop",
  pitchRange: [-1, 1],
  fn(v) {
    v({ freq: 400, dur: 0.1, gain: 0.3 })
  }
})

Your favicon is probably bigger
than this entire library.
Ship sounds already.