WAV
WAV test files generated in your browser
Generate deterministic sine, square, noise, and silence WAV fixtures locally for playback, waveform, VAD, duration, and upload tests.
Verified . 44.1 kHz, 16-bit mono PCM samples, RIFF chunks, deterministic seed, waveform, and Blob playback are browser-tested.
Identify WAV files
- Magic signature
52 49 46 46 · 57 41 56 45 (RIFF…WAVE)- Registered MIME
audio/wavaudio/waveaudio/x-wav
WAV is a RIFF container. The fmt and data chunks define the PCM representation and declared payload length.
Choosing WAV
PCM WAV is broadly decoded by browsers and audio tools and is useful when a predictable uncompressed signal matters.
Generate the shortest signal that covers the test.
Use silence and reference tones to test thresholds separately.
Common test cases
- Waveform rendering
- Voice activity thresholds
- Exact-size audio uploads
Invalid and boundary cases
- Trailing NULL padding
- Declared RIFF size differs from physical size
- Silence and deterministic noise
WAV fixture matrix
This format is generated locally in the browser and does not receive a permanent SEO URL.
Open the local generatorGenerate and verify locally
These examples use the browser generator because this format has no permanent downloadable SEO URL. Generate the bytes locally, then validate the physical size and format signature before using the fixture in an upload, rendering, waveform, or conversion test.
Generate the same exact-size PCM WAV twice and compare SHA-256 values. The expected result is two identical 128 KiB files; the test fails on non-deterministic samples, inaccurate NULL padding, a missing RIFF/WAVE signature, or any byte drift.
import { test, expect } from "@playwright/test";
import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";
test("generate deterministic exact-size WAV fixtures", async ({ page }) => {
await page.goto("https://testfiles.dev/#generator");
await page.locator("#gType").selectOption("wav");
await page.locator("#gDur").fill("1");
await page.locator("#gFill").selectOption("kb");
await page.locator("#gSize").fill("128");
const generate = async (name: string) => {
await page.locator("#gName").fill(name);
const download = page.waitForEvent("download");
await page.locator("#gGo").click();
const result = await download;
const path = await result.path();
if (!path) throw new Error("Browser did not expose the WAV download");
return readFile(path);
};
const first = await generate("tone-a.wav");
const second = await generate("tone-b.wav");
expect(first).toHaveLength(128 * 1024);
expect(first.subarray(0, 4).toString("ascii")).toBe("RIFF");
expect(first.subarray(8, 12).toString("ascii")).toBe("WAVE");
expect(createHash("sha256").update(first).digest("hex"))
.toBe(createHash("sha256").update(second).digest("hex"));
});Inspect RIFF/WAVE identifiers and the little-endian declared RIFF length. Exact-size fixtures may contain trailing NULL padding, so the declared container length may be smaller than the physical file but must never exceed it.
import { readFile } from "node:fs/promises";
const path = process.argv[2];
if (!path) throw new Error("Usage: node verify-wav.mjs <fixture.wav>");
const bytes = await readFile(path);
if (bytes.subarray(0, 4).toString("ascii") !== "RIFF" ||
bytes.subarray(8, 12).toString("ascii") !== "WAVE") {
throw new Error("The file is not a RIFF/WAVE stream");
}
const declaredRiffBytes = bytes.readUInt32LE(4) + 8;
if (declaredRiffBytes > bytes.length) {
throw new Error("RIFF declares more bytes than the physical file contains");
}
console.log({
physicalBytes: bytes.length,
declaredRiffBytes,
trailingPaddingBytes: bytes.length - declaredRiffBytes,
});