@stemplayer-js/stemplayer-js
v4.1.0-beta.4
Published
A streaming, low latency Stem Player Web-Component
Maintainers
Readme
<stemplayer-js>
A streaming, low latency Stem Player Web-Component

See this live example of our stem player
This webcomponent follows the open-wc recommendation.
Architecture (Current)
The player is split across four custom elements:
stemplayer-js: orchestration, playback lifecycle, keyboard handling, UI tick loop.stemplayer-js-controls: top control strip (play/pause, loop, seek, time, zoom).stemplayer-js-stem: per-track row (solo/mute/volume + waveform).stemplayer-js-workspace: timeline overlay for region selection, cursor, and region gestures.
Component Distribution
- Playback engine and segment scheduling remain in
@firstcoders/hls-web-audio. - High-frequency visual updates (
currentTime,currentPct) are rendered instemplayer-js. - The workspace is an overlay sibling (not a structural wrapper around rows), reducing nesting depth and avoiding layout coupling.
Flow Overview
stemplayer-jscreates the sharedAudioControllerand loads each slottedstemplayer-js-stem.- On playback, a UI tick loop in
stemplayer-jsreads controller time/pct. - Tick values are pushed directly to controls/stems.
stemplayer-js-controlsandstemplayer-js-stemupdate their UI imperatively for hot-path values.stemplayer-js-workspacehandles regions/cursor interactions and emitsregion:*events.
This keeps scheduling concerns in the audio package while keeping visual progress updates in the UI layer.
Contributing
This repo is a subtree split of our monorepo. Using a monorepo greatly simplifies development of many packages with dependencies. If you'd like to contribute to the development of stemplayer-js, please create a pull-request there.
Installation
npm i @stemplayer-js/stemplayer-jsUsage
<script type="module">
import '@stemplayer-js/stemplayer-js/element.js';
</script>
<stemplayer-js>
<stemplayer-js-controls label="A label"></stemplayer-js-controls>
<stemplayer-js-stem
label="Drums A"
src="https://your-cdn-com/drums.m3u8"
waveform="https://your-cdn-com/drums.json"
volume="0.1"
>
</stemplayer-js-stem>
<stemplayer-js-stem
label="Vocals"
src="https://your-cdn-com/vocals.m3u8"
waveform="https://your-cdn-com/vocals.json"
muted="true"
volume="0.2"
></stemplayer-js-stem>
</stemplayer-js>See here for further options, events and CSS variables
Browser Support
The Player works in browsers supporting the Web Audio API. This includes most modern browsers.
The stem player is built as a web-component which is supported natively by most modern browsers.
For targeting older browsers, you can utilise your own build system.
Polyfills for web-components exist for support for older browsers.
Audio
The player consumes m3u8 playlist files known from the HLS protocol.
The audio is split up into chunks and served (over simple HTTP) separately.
Why HLS and not just download whole files? Downloading and decoding, for example, 10 5minute audio files will consume bandwith and bloat memory: each minute of every audio file worth of mp3 data is decoded into 44k PCM data and will consume roughly 100mb. By using live streaming we not only speed up playback, we also reduce the memory footprint.
Why not progressive download? We need to use the web audio API to achieve precise synchronized playback.
Performance Notes
- Use pre-generated waveforms and segmented audio to minimize startup and memory pressure.
- UI progress is driven by the player UI loop; this avoids coupling high-frequency render work to engine internals.
See also
- https://ffmpeg.org/ffmpeg-formats.html#toc-hls-1
- https://ffmpeg.org/ffmpeg-formats.html#toc-segment_002c-stream_005fsegment_002c-ssegment
See also this Docker image to help you segment your audio.
If you have an AWS environment, we have also created a Serverless Backend that will do this for you.
Waveforms
Because we don't download the entire audio file, we cannot analyse the audio so that we can display a nice waveform. So unfortunately these also need to be pre-generated. Although inconvenient, it is probably good practice anyway as a waveform in json format is very small in size; there is no need to re-compute it time and time again.
See here for info on how to generate compatible waveforms. Make sure you limit the --pixels-per-second to around 20, since by default the library will output that contains too much detail.
The output will have to be normalized so the waveform will be represented by an array of numbers that is between -1 and +1.
See here for a Docker image which should (hopefully) help.
If you have an AWS environment, we have also created a Serverless Backend that will do this for you.
Linting and formatting
To scan the project for linting and formatting errors, run
npm run lintTo automatically fix linting and formatting errors, run
npm run formatTesting with Web Test Runner
To execute a single test run:
npm run testTo run the tests in interactive watch mode run:
npm run test:watchTooling configs
For most of the tools, the configuration is in the package.json to minimize the amount of files in your project.
If you customize the configuration a lot, you can consider moving them to individual files.
Local Demo with web-dev-server
npm startTo run a local development server that serves the basic demo located in demo/index.html
