Generate Dual-Tone Multi-Frequency tones from text or digit sequences using the Web Audio API. Zero dependencies.
Built on the RFC 2833 and ITU E.161 standards.
import Tone from './tone.js';
const tone = new Tone();
// From digits
const [channelData, sampleRate] = await tone.create([1, 2, 3]);
// From text — letters are mapped to keypad digits per E.161
const [channelData, sampleRate] = await tone.create('hello');create accepts a string or array of characters. Letters are converted to their corresponding phone keypad digit (h→4, e→3, l→5, o→6), digits and *, #, are used as-is, and unrecognized characters are silently dropped.
Returns [Float32Array, number] — the raw PCM channel data and the sample rate. You bring your own encoder to convert the output into whatever format you need.
Pair with ArraybufferToWav to get a playable Blob:
import Tone from './tone.js';
import arrayBufferToWav from '../ArraybufferToWav/lib.js';
const tone = new Tone();
const [channelData, sampleRate] = await tone.create('hello');
const blob = arrayBufferToWav(channelData, sampleRate);
// Play it
const audio = new Audio(URL.createObjectURL(blob));
audio.play();Each key produces two simultaneous sinusoidal tones — one from the row frequency, one from the column:
1209 Hz 1336 Hz 1477 Hz
697 Hz 1 2 ABC 3 DEF
770 Hz 4 GHI 5 JKL 6 MNO
852 Hz 7 PQRS 8 TUV 9 WXYZ
941 Hz * 0 #
Each tone uses an 80 ms mark (sound) followed by an 80 ms space (silence), giving 160 ms per digit. A 10-digit phone number takes ~1.6 s to render.
Creates a DTMF generator instance.
Renders the full DTMF sequence and returns a Promise<[Float32Array, number]> — the PCM samples and sample rate (44100 Hz).
| Parameter | Type | Description |
|---|---|---|
input |
string | Array |
Digits, letters, *, #, or spaces. Letters are mapped to keypad digits per E.161. |
Creates the oscillator pair for a single key and connects them through a low-pass filter (8 kHz cutoff) to the audio context destination.
Generator that schedules each tone in the sequence on the given OfflineAudioContext, yielding each key as it's scheduled.
Filters input to only values present in compare.
getIncluded([1, 2, 3], [1, 3]); // [1, 3]Maps each character in input through a lookup object, passing through unmapped values.
getCollection('abc', { a: 1, b: 2, c: 3 }); // [1, 2, 3]