Procedural UI sounds for the web. Zero audio files. Pure synthesis.
Every native app has satisfying sounds — iOS toggle clicks, macOS trash crumple, Android keyboard taps. Web apps have nothing. tiks brings that missing sensory layer using the Web Audio API. Every sound is generated at runtime through oscillators, noise buffers, and gain envelopes. No audio files shipped. Just math.
All sounds share a common synthesis engine with a unified theme — so they sound like they belong together.
npm install @rexa-developer/tiksimport { tiks } from '@rexa-developer/tiks'
// Initialize (safe to call at any time — the AudioContext is created on the
// first real user gesture, so this never triggers an autoplay-policy warning)
tiks.init()
// Play sounds
tiks.click()
tiks.success()
tiks.toggle(true)That's it. Three lines.
| Method | Character | Duration |
|---|---|---|
click() |
Short, crisp tap | ~12ms |
toggle(true) |
Rising pitch snap | ~60ms |
toggle(false) |
Falling pitch snap | ~60ms |
success() |
Two-note rising chime | ~200ms |
error() |
Gentle buzz/thud | ~150ms |
warning() |
Cautious double-tap | ~180ms |
hover() |
Whisper-soft tick | ~15ms |
pop() |
Playful bubble pop | ~80ms |
swoosh() |
Quick transition whoosh | ~120ms |
notify() |
Bright attention ping | ~300ms |
hover()is a high-frequency primitive — use sparingly. Hover fires constantly as the pointer moves, and audio on every hover causes fatigue. Reserve it for deliberate, low-density targets (and debounce it); never bind it to dense lists, menus, or whole-page hover regions.
Two built-in themes that change the character of every sound:
tiks.init({ theme: 'soft' }) // Warm, rounded, Apple-like (default)
tiks.init({ theme: 'crisp' }) // Sharp, tactile, mechanical keyboard feelSwitch at runtime:
tiks.setTheme('crisp')import { tiks, defineTheme } from '@rexa-developer/tiks'
const brand = defineTheme({
name: 'brand',
baseFreq: 500,
oscType: 'triangle',
decay: 0.8,
})
tiks.init({ theme: brand })Theme properties:
| Property | Type | Description |
|---|---|---|
name |
string |
Theme identifier |
baseFreq |
number |
Root frequency all sounds derive from |
noiseColor |
'white' | 'pink' |
Noise type for transients |
oscType |
OscillatorType |
'sine' 'triangle' 'square' 'sawtooth' |
filterFreq |
number |
Bandpass center for click/transient sounds |
filterQ |
number |
Filter resonance |
attack |
number |
Attack time in seconds |
decay |
number |
Decay multiplier (1.0 = normal, 0.5 = snappy) |
brightness |
number |
Highpass cutoff (higher = brighter) |
tiks.setVolume(0.5) // 0.0 - 1.0 (default: 0.3)
tiks.mute()
tiks.unmute()
tiks.setTheme('crisp')tiks.init({
theme: 'soft', // 'soft' | 'crisp' | custom theme
volume: 0.4, // 0.0 - 1.0
muted: false, // Start muted
respectReducedMotion: true, // Default: true. Set false to always play.
})import { useTiks } from '@rexa-developer/tiks/react'
function ToggleButton() {
const { click, toggle } = useTiks()
return (
<button onClick={() => {
click()
setEnabled(prev => {
toggle(!prev)
return !prev
})
}}>
Toggle
</button>
)
}The hook auto-initializes on mount. All returned methods are stable references.
Note:
volumeandmutedare shared globally across alluseTikshooks (they live on the underlying audio engine).themeis per-hook. If two hooks pass differentvolumevalues, the last-mounted one wins.
<script setup lang="ts">
import { useTiks } from '@rexa-developer/tiks/vue'
const { click, success, error } = useTiks()
</script>
<template>
<button @click="click">Click me</button>
</template>The composable auto-initializes on component mount. Same global/per-instance semantics as the React hook: volume and muted are shared across all useTiks calls, theme is per-composable.
For minimal bundles, import only the sounds you need:
import { init, click, success } from '@rexa-developer/tiks'
init()
click()
success()<script type="module">
import { tiks } from 'https://esm.sh/@rexa-developer/tiks'
document.querySelector('#btn').onclick = () => tiks.click()
</script>prefers-reduced-motion: Respected by default — auto-mutes and reacts to runtime preference changes. PassrespectReducedMotion: falseto opt out.- Sounds are additive: Never the only way to convey information — always pair with visual feedback
- Global mute:
tiks.mute()for user control - Default volume is 30%: Subtle, not intrusive
Works in all browsers that support the Web Audio API — Chrome, Firefox, Safari, Edge, and mobile browsers. The library handles autoplay policy automatically by resuming the AudioContext on first interaction.
Each sound is a pure function that creates Web Audio nodes, connects them, schedules playback, and lets the browser garbage-collect them:
OscillatorNode ──┐
├──→ GainNode (envelope) ──→ MasterGain ──→ Limiter ──→ speakers
NoiseBuffer ─────┘
↓
FilterNode ───────────────────────────────┘
A transparent limiter sits on the master bus so overlapping sounds during rapid interaction can't hard-clip; it stays inactive at normal levels.
No audio files. No downloads. No decoding. Just oscillators and math.
| Entry | Gzipped |
|---|---|
Core (tiks) |
~2KB |
React hook (tiks/react) |
~300B |
Vue composable (tiks/vue) |
~300B |
MIT