New — WaveformSounds: search, filter and audition a whole sample pack from one list. Try it →

WaveformSounds

Audition a whole sample pack from one list.

A searchable, filterable list of sound previews — a mini waveform per row, type, key and BPM filters, and arrow-key auditioning. Every row plays through one shared player, so a pack of hundreds costs the same as one.

$ npm i @arraypress/waveform-sounds
GitHub

Play a sound below, then press ↓.

Live demo

33 previews, one player.

A real pack of 7 types — plucks, leads, pads, keys, bass and loops. Search it, filter by type, key, tempo or loops and one-shots, sort it, and audition it from the keyboard. The filters write themselves into the address bar, so a filtered view is a link you can share.

↑↓ next sound ←→ seek Space play / pause / search

33 sounds

  • Paris Bass Loop 01LoopBass0:06
  • Paris Bass Loop 02LoopBass0:06
  • Paris Bass Note A1Bass0.4s
  • Paris Bass Note A2Bass0:02
  • Drum LoopLoopDrum loops1280:03
  • Paris Drums 01LoopDrum loops0:06
  • Paris Drums 02LoopDrum loops0:06
  • Electric PianoKeys1260:07
  • House PianoKeys126C#m0:07
  • Red WoodsLeadsG#m0:13
  • Surprise KissLeads0:07
  • Too YoungLeads1210:07
  • Paris Loop 01LoopMelodic loopsAm0:06
  • Paris Loop 02LoopMelodic loopsAm0:03
  • Paris Loop 03LoopMelodic loopsAm0:03
  • ColorizePads0:13
  • ReachPads0:07
  • SeacrestPads0:07
  • Too YoungPads0:07
  • Bus 605Plucks0:07
  • DesirePlucks0:11
  • Got Me ThinkingPlucksG#m0:11
  • IcarusPlucks0:12
  • LovelyPlucks0:11
  • LuminousPlucksDm0:12
  • NocturnePlucksF#0:12
  • Pieces Of YouPlucks0:12
  • PlankPlucks121Eb0:07
  • SinePlucksCm0:11
  • Small MomentsPlucks0:11
  • Spaces In BetweenPlucks121Bb0:07
  • Too YoungPlucks121Gm0:07
  • WingsPlucksG0:11

Every preview is a clip from this site's own demos. BPM and key are written into the file names only where two separate analyses of the audio agreed, and left out where they didn't — so some rows have no tempo or key, as in any real pack.

Built for auditioning

A sound browser, not a stack of players.

The way producers actually shop for sounds: narrow the list, then run down it with the arrow keys, hearing each one the moment it's selected.

One engine, any number of rows

Rows are pictures — low-res peaks, drawn only when on screen. A single WaveformPlayer plays whichever sound you pick, so a 300-sound pack is still one player.

Forgiving search

Every word, in any order, typo-tolerant. A number matches the BPM. Press / in the list to jump to the search box.

Type, key and BPM filters

Type chips (a searchable menu once there are more than ten), a key menu and a BPM range — each appears only when the data has something to filter.

Keyboard auditioning

↑ / ↓ move through the list — and while a sound plays, play the next one. ← / → seek, Space plays and pauses.

Loops & one-shots

Mark a sound loop: true and it gets a loop icon, the list gets a Loops / One-shots filter, and the Loop toggle repeats only loops — a kick never loops.

Sort & paging

Sort by title, BPM, key or length — pick which orders the menu offers. Long lists page with “Show more” (50 rows by default).

Filters in the URL

urlState keeps the search, type, key, BPM range and sort in the address, so a filtered list can be shared and survives a refresh.

Server-rendered

renderSounds() writes the toolbar and every row as HTML; the runtime adopts that markup instead of rebuilding it.

Optional downloads

Give a sound a download URL — a free sample, the .mid of a MIDI preview — and its row gets a download button. Rows without one carry nothing.

Colour-agnostic

Everything derives from the text colour and the "on" states invert, so it fits light and dark pages as is. --ws-accent opts into a brand colour.

Mobile layout

On a narrow screen each row becomes a title, a meta line and a full-width waveform — still one tap to play, one tap to seek.

Play stats for free

The list plays through a regular WaveformPlayer, so WaveformTracker counts which sounds get played with no extra code.

The manifest

From a folder of previews to a list in one command.

WaveformGen walks the folder and writes one sounds.json for the whole pack: the type is the sub-folder, the title, BPM and key come from the file name — and are left out rather than guessed — and a "loop" in the folder or file name marks a loop. Each sound carries 64 bars of peaks, so every row draws its waveform without decoding any audio. This is the command that built the demo above.

terminal
$ npx @arraypress/waveform-gen ./public/previews/ --recursive \
    --manifest ./public/previews/sounds.json --base-url /previews/

  📋 ./public/previews/sounds.json: 33 sounds, 7 with BPM, 13 with key
file names → metadata
public/previews/
├── Drum loops/
│   └── Drum_Loop_128.mp3           → Drum Loop · 128 BPM · loop
├── Keys/
│   └── House_Piano_126_C#min.mp3   → House Piano · 126 BPM · C#m
├── Plucks/
│   ├── Plank_121_Ebmaj.mp3         → Plank · 121 BPM · Eb
│   └── Wings_Gmaj.mp3              → Wings · G
└── Bass/
    └── Paris_Bass_Loop_01.mp3      → Paris Bass Loop 01
public/previews/sounds.json
{
  "version": 1,
  "sounds": [
    {
      "url": "/previews/Keys/House_Piano_126_C%23min.mp3",
      "title": "House Piano",
      "type": "Keys",
      "bpm": 126,
      "key": "C#m",
      "duration": 7.63,
      "peaks": "9285748e926ba6a4a1cbd8b8…"
    },
    {
      "url": "/previews/Drum%20loops/Drum_Loop_128.mp3",
      "title": "Drum Loop",
      "type": "Drum loops",
      "bpm": 128,
      "loop": true,
      "duration": 3.75,
      "peaks": "a585883a912a1508ff8c883f…"
    },
    …
  ]
}

--manifest needs @arraypress/waveform-gen 2.1.0 or later; marking loops, 2.2.0.

Two layouts

Plain rows, one player docked below.

player="strip" drops the per-row waveforms and docks a full player under the list. And every control is optional — here, just the 14 plucks, a key filter and no tempo anywhere. Both lists share one player class, so starting one pauses the other.

14 sounds

  • Bus 6050:07
  • Desire0:11
  • Got Me ThinkingG#m0:11
  • Icarus0:12
  • Lovely0:11
  • LuminousDm0:12
  • NocturneF#0:12
  • Pieces Of You0:12
  • PlankEb0:07
  • SineCm0:11
  • Small Moments0:11
  • Spaces In BetweenBb0:07
  • Too YoungGm0:07
  • WingsG0:11

Plucks.astro
<WaveformSounds
  sounds={plucks}
  player="strip"
  filters={['key']}
  sorts={['default', 'title', 'key']}
  columns={['key', 'duration']}
  loopToggle={false}
/>

Getting started

One element and a manifest.

Point data-waveform-sounds at a manifest and load the player and the list — it initialises on page load. Or use the typed wrappers for Astro, React, Vue and Svelte, which server-render the list when you pass sounds.

<!-- The player is the audio engine — load it first -->
<link rel="stylesheet" href="https://unpkg.com/@arraypress/waveform-player/dist/waveform-player.css">
<link rel="stylesheet" href="https://unpkg.com/@arraypress/waveform-sounds/dist/waveform-sounds.css">
<script src="https://unpkg.com/@arraypress/waveform-player/dist/waveform-player.min.js"></script>
<script src="https://unpkg.com/@arraypress/waveform-sounds/dist/waveform-sounds.min.js"></script>

<!-- One element: the runtime fetches the manifest and builds the list -->
<div data-waveform-sounds
     data-manifest="/previews/sounds.json"
     data-url-state="true"></div>

Start shipping beautiful audio.

Free, open-source and ~12KB — add a div and go.

Read the docs

API reference, guides and recipes for all nine packages.

Browse docs

Open the builder

Configure a player visually and copy the HTML, JS or React.

Try it

Star on GitHub

MIT-licensed, open source, zero dependencies.

View source