npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@arraypress/waveform-sounds-astro

v0.2.1

Published

Astro component wrapper for @arraypress/waveform-sounds — a server-rendered, searchable, filterable sound list with typed props and ClientRouter support.

Readme

Waveform Sounds for Astro

Typed Astro component for @arraypress/waveform-sounds. A searchable, filterable list of sound previews — server-rendered, then brought to life — with a mini waveform per row and one shared audio engine.

npm version license

Documentation · npm


Install

npm install @arraypress/waveform-sounds-astro @arraypress/waveform-sounds @arraypress/waveform-player

Import the two stylesheets once, in your layout. The component does not inject CSS (none of the family's wrappers do):

---
import '@arraypress/waveform-player/styles.css';
import '@arraypress/waveform-sounds/styles.css';
---

The JavaScript is handled for you: the component ships a bundled client script that loads the sounds runtime, and the player too unless the page already has one (see below).

Usage

From an array — rendered on the server

---
import WaveformSounds from '@arraypress/waveform-sounds-astro';
---
<WaveformSounds
  sounds={[
    { url: '/previews/kick-01.mp3', title: 'Kick 01', type: 'Drums', bpm: 128, key: 'F minor', duration: 2.4 },
    { url: '/previews/bass-04.mp3', title: 'Bass Loop 04', type: 'Bass', bpm: 128, key: 'Fmin', peaks: 'a1b2c3…' },
  ]}
  pageSize={100}
  columns={['type', 'bpm', 'key']}
/>

The whole list — toolbar, filter chips, every row — is in the HTML. The browser runtime adopts that markup instead of rebuilding it, so the list is readable and crawlable before any script runs. Give rows peaks (what waveform-gen --manifest writes) and the row waveforms draw without decoding any audio. A sound with download: '/files/kick-01.wav' gets a download button on its row (a plain <a download> — gating is up to your site).

From a manifest — fetched in the browser

<WaveformSounds manifest="/sounds.json" player="strip" />

With only manifest (and no sounds), the component renders an empty container with data-manifest, and the runtime fetches and renders the list. sounds={[]} counts as "given" — it renders an empty list, as the core does.

Generate a manifest from a folder of previews (--base-url is the folder's public URL; without it every URL is relative to the site root, /kick.mp3):

npx @arraypress/waveform-gen ./public/previews/*.mp3 --manifest ./public/sounds.json --base-url /previews/

To server-render a manifest instead, read it at build time and pass its sounds array.

Props

Every prop is optional. An omitted prop emits nothing, so the core's default applies. Types come straight from the core's WaveformSoundsOptions.

| Prop | Type | Default | Notes | |---|---|---|---| | sounds | SoundInput[] | — | Rendered on the server. | | manifest | string | — | URL, fetched in the browser when there are no sounds. | | player | 'inline' \| 'strip' | 'inline' | Mini waveform per row, or one docked player. | | search | boolean | true | Search box. | | filters | ('type' \| 'key' \| 'bpm')[] | all three | [] = no filter controls. Drop one from filters, columns and sorts to remove it everywhere. | | sorts | SoundsSort[] | all five | Orders the Sort menu offers; the first is the start order. [] = no menu. | | showCount | boolean | true | The "12 of 300 sounds" line. | | menuSearch | number | 8 | Type / key / sort dropdowns with more options than this get a search field. | | urlState | boolean \| string | false | Keep filters + sort in the address (?q=…&type=…), shareable and refresh-proof. A string prefixes the parameter names, for several lists on a page. | | idPrefix | string | the id, else a hash of the sounds | Prefix for the dropdowns' element ids. Two lists of the same sounds on one page need an id or idPrefix each. | | loopToggle | boolean | true | Loop button. | | pageSize | number | 50 | Rows before "Show more"; 0 = all. | | maxTypeChips | number | 10 | More types than this become a menu. | | columns | ('type' \| 'bpm' \| 'key' \| 'duration')[] | all four | Order is kept. | | waveformStyle | 'mirror' \| 'bars' | 'mirror' | Row waveform. | | waveformColor | string | CSS --ws-wave-color | Default derives from currentColor. | | progressColor | string | CSS --ws-progress-color | Default derives from currentColor. | | barWidth | number | 2 | Row bar width, CSS px. | | barGap | number | 1 | Gap between row bars, CSS px. | | loop | boolean | false | Start with Loop on. | | autoAdvance | boolean | false | Play the next visible sound at the end. | | arrowAudition | boolean | true | ↑/↓ play the next row while playing. | | strings | Partial<WaveformSoundsStrings> | English | UI words; emitted as JSON. | | playerOptions | object | — | Options for the engine WaveformPlayer; emitted as JSON (functions are dropped). | | id | string | — | Container id. | | class | string | — | Added to waveform-sounds waveform-sounds--<player>. |

Not available as props: the on* callbacks (see Events) and playerClass (the page's window.WaveformPlayer is used — see below).

Theming

No accent is needed: like the rest of the family the list is colour-agnostic. Everything derives from currentColor, so it fits light and dark pages as is, and the "on" states (selected chip, playing row) invert. Opt into a brand colour with --ws-accent / --ws-on-accent if you want one.

Set --ws-surface to your page background. The runtime detects it, but a server-rendered list paints before any script runs, so the first paint (dropdown and button fills) uses the CSS value:

.waveform-sounds { --ws-surface: var(--color-bg); }

Other properties: --ws-wave-color, --ws-progress-color, --ws-border, --ws-radius, --ws-control-radius, … (see the core's README).

Events

Callbacks can't cross server rendering. Listen for the core's bubbling DOM events instead — on the element, or on document:

<WaveformSounds id="pack" manifest="/sounds.json" />

<script>
  document.addEventListener('waveformsounds:play', (e) => {
    console.log('playing', e.detail.sound.title);
  });
</script>

| Event | detail | |---|---| | waveformsounds:ready | { sounds, instance } | | waveformsounds:play / :pause / :end | { sound, index, instance } | | waveformsounds:filter | { visible, total, filter, sort, instance } | | waveformsounds:error | { error, sound?, index?, instance } |

For the imperative API, window.WaveformSounds.getInstance('#pack') returns the instance (play(), setFilter(), setSort(), …). It is created by the client script, so call it from an event handler or after waveformsounds:ready.

Content Security Policy

The client script is a processed Astro <script>: one bundled module, emitted as an external file (/_astro/…) and identical on every page — no is:inline, no define:vars. Everything per-instance travels in data-* attributes. A strict hash-based CSP needs no extra hashes for this component.

View Transitions (<ClientRouter />)

Works with and without Astro's router. The script initialises on load, again on every astro:page-load, and on astro:after-swap destroys the instances whose element left the page (stopping their audio). A list inside a transition:persist element is carried over and keeps playing.

Using it next to other players (WaveformBar, <WaveformPlayer>, …)

The list plays through a WaveformPlayer, and singlePlay (one sound at a time across the page) only works between players of the same class. So the client script never replaces a player the page already has: it waits for the page's own scripts to run (DOMContentLoaded) and uses window.WaveformPlayer if it's there — a persistent WaveformBar's, or one loaded by a <script> tag. Only when there is none does it load the bundled @arraypress/waveform-player (a separate chunk, by dynamic import).

A player your code loads LATER than that (from its own dynamic import) can't be detected; load it from a static <script>/import instead.

License

MIT © ArrayPress