Skip to content

Sounds

The Astro, React, Svelte and Vue wrappers (@arraypress/waveform-sounds-astro, -react, -svelte, -vue) version separately — see Framework wrappers.

  • The strip’s docked player no longer starts empty. It showed a play button, no waveform and 0:00 / 0:00, and its button did nothing. The first sound the list shows is now cued in it — title, waveform, length — without playing (metadata only, the browser default for any <audio>), and its button plays it. Its row isn’t marked until it plays, current stays null, next() starts with it, and a filter change re-cues the first sound still shown until something plays.
  • The docked player stays in place. It was position: sticky, so it slid along the bottom of the screen over the rows as the page scrolled. It’s static now; --ws-strip-position: sticky brings the old behaviour back for a long list.
  • Rows and the player agree on lengths. Rows rounded to the nearest second and the player’s clock rounds down, so a 7.6s sound read 0:08 in its row and 0:07 in the player. Rows round down too now.
  • BPM is a menu with a two-handle range instead of two number fields. The button reads Any BPM, or 120–128 BPM once set; its panel holds a range across the pack’s own tempos (two native range inputs on one track: arrow keys, screen readers and touch work as they do on any slider), the live readout and an Any BPM reset. The label follows the drag; the list re-filters once it settles. A handle left at an end is no limit on that side, so a range nobody narrowed never hides the sounds without a BPM. ?bpm= in the address and setFilter({bpmMin, bpmMax}) work as before. New strings: anyBpm, bpmRange.
  • On a list that marks loops, Loop starts on. Only loops repeat (as in 0.2.0), so loops repeat out of the box and one-shots still play once. The loop option now defaults to null (follow the data); true / false still force it. A list that marks nothing starts with it off, as before.
  • .ws-bpm-range and its number inputs (data-ws-bpm-min / -max are now the range handles).
  • Loops and one-shots. A sound can be marked loop: true; anything else is a one-shot. Marking loops does three things:
    • a small loop icon beside the name (with “Loop” for screen readers);
    • an All / Loops / One-shots filter, offered only when the list has both kinds (filters entry 'loop', on by default; in the address as ?loop=loop / ?loop=one-shot with urlState);
    • the Loop toggle repeats only loops. A one-shot always plays once. A list that marks no loops behaves exactly as before.
  • isLoop() and LOOP_FILTERS in the data module; facets() reports loops and oneShots; four new strings (loopFilter, loops, oneShots, isLoop).
  • The title cell wraps its text in .ws-title-text (so a loop’s icon can sit after an ellipsised name). Styling .ws-title still works.
  • Sounds under a second show their length. formatDuration rounded to whole seconds, so a 0.38s one-shot read 0:00 — which looks like a missing length, and one-shots (kicks, hats, single notes) are routinely that short. Under a second it now shows tenths: 0.4s.
  • The strip player matches a dark page. Its background fell back to the system Canvas colour, which is white on a dark page that doesn’t declare color-scheme: dark — a white box under a dark list. It now falls back to --ws-surface (the measured page surface), as the dropdowns already did.
  • No stray · on a narrow screen. The mobile meta line put a separator before a cell whenever any cell came first, even an empty one, so a sound with no key read · 0:08. It now follows only a cell with content.
  • The strip layout keeps its docked styling. The engine was mounted on the .ws-engine slot itself, and WaveformPlayer resets its container’s className, so ws-engine--strip was wiped on the first play: no sticky position, border, padding, --ws-strip-bg or --ws-radius. The engine now mounts into a child of the slot ([data-ws-engine-mount], made at runtime, so server-rendered markup from 0.1.0/0.1.1 works unchanged) and destroy() removes it.
  • render.d.ts declares escapeHtml, which /render has always exported.
  • The playing row’s progress is smooth. It was painted a whole bar at a time (a bar switched colour once the playhead passed its middle), so a short clip advanced in visible steps — an 8-second loop across ~120 bars moved ~15 times a second however often it was redrawn. The bars are now drawn twice, the second pass clipped at the exact playhead pixel: the played part grows through the middle of a bar. Measured in Chrome: the edge moves every ~1px (≈37 times a second on a 12-second clip at 60fps).
  • Bar geometry is a pure, tested function (barRects), and each pass is a single fill() of a Path2D instead of one fillRect per bar.
  • First release: a searchable, filterable list of sounds for showcasing the previews inside a sample pack, preset bank or sound library.
  • One WaveformPlayer (self mode) is the audio engine for the whole list, so playback gets the player’s seeking (including hosts that ignore byte ranges), error handling, Media Session card and singlePlay hand-off.
  • Two layouts: inline (a mini waveform per row; the playing row fills and is click-to-seek) and strip (plain rows, the engine docked below).
  • Search (every word, any order; a number matches the BPM), type chips (a menu past maxTypeChips), key menu, BPM range, sort, Loop toggle, paging.
  • Keyboard: ↑/↓ move and, while a sound plays, audition the next; ←/→ seek; / focuses the search; ↓ from the search enters the list.
  • Peaks as an 8-bit hex string (two characters per bar).
  • @arraypress/waveform-sounds/render: a DOM-free renderer for server rendering; the runtime adopts its markup instead of rebuilding it.
  • @arraypress/waveform-sounds/no-autoinit.
  • Dropdowns for type (past maxTypeChips), key and sort: a button and a popup listbox, with a search field past menuSearch (8) options; ↑/↓, Enter, Esc, type to narrow (WAI-ARIA combobox/listbox).
  • Every control is optional: search, filters, sorts (which orders, in order; the first is the starting order), loopToggle, showCount, columns.
  • Optional per-sound download link (a download button on that row only).
  • urlState: the filters and sort live in the address (shareable, survive a refresh), optionally prefixed for several lists on a page.
  • The engine is built when the list is ready (no audio loaded), so waveform-tracker and other waveformplayer:ready hooks see the first play.
  • Colour-agnostic by default: the “on” states are inverted (text colour as fill, page surface as ink). The surface is detected (--ws-surface) and re-read on theme flips; set it yourself for the first server paint.