@kano/stem-daw
v0.1.2
Published
Stem DAW UI — React components, controller, and stores for the STEM beat-quantized DAW. Built on top of @kano/stem-web-audio-player.
Maintainers
Keywords
Readme
@kano/stem-daw
Stem DAW UI — React components, controller, and stores for the STEM
beat-quantized DAW. Renders the full DAW timeline, transports, sample
search, autotune & beats lab panels. Builds on top of the audio engine
that ships separately as @kano/stem-web-audio-player (this package
bundles the engine internals it needs, so you do not have to
install the engine package alongside).
This package is consumed by stem-player-rd behind the /studio
route. The same source tree also builds the standalone DAW web app at
daw.html (see vite.config.daw.ts).
CONSTRAINT Publishing is purely a packaging change. The runtime behaviour —
DAWView, theDAWControllerstate machine, theStemFmMixConfigimport contract — is identical to the standalone build.
Local development (same pattern as @kano/stem-web-audio-player)
This is the workflow we use day-to-day. No npm publish required.
Option A — sibling file: install (recommended in the monorepo)
With stem-player-rd and stem-studio as siblings under ~/Code/:
# stem-studio — build the library once, rebuild after DAW changes
cd stem-studio
yarn install
yarn build:lib
# optional: rebuild on save
yarn watch
# stem-player-rd — point at the sibling package (package.json):
# "@kano/stem-daw": "file:../stem-studio"
cd ../stem-player-rd
npm install
STEM_DAW_LOCAL=1 npm run prod # hot-reload from stem-studio *source*file:../stem-studio resolves through this repo's package.json exports
→ dist/. Run yarn build:lib (or yarn watch) whenever you change DAW
code and are not using STEM_DAW_LOCAL=1.
Option B — yarn link
# stem-studio
yarn link
yarn install && yarn build:lib && yarn watch
# consumer (e.g. stem-player-rd)
yarn link @kano/stem-daw
yarn install
yarn prod # or your app's dev scriptOption C — live source in the consumer (like STEM_ENGINE_LOCAL)
In stem-player-rd, set STEM_DAW_LOCAL=1 or VITE_STEM_DAW_LOCAL=1.
Vite aliases @kano/stem-daw/* to ../stem-studio/src/daw/* and serves
worklets from stem-studio/public/workers/ — no rebuild between edits.
Install from npm (when published)
yarn add @kano/stem-daw
yarn add react@^19 react-dom@^19 react-router-dom@^7 zustand@^5Peer dependencies (must be provided by the consumer)
| Package | Range | Why |
| ------------------ | ------- | ------------------------------------------------------ |
| react | ^19 | DAW UI |
| react-dom | ^19 | DAW UI |
| react-router-dom | ^7 | Used by some panels for in-app navigation |
| zustand | ^5 | DAW stores; must be the same instance as your app |
Other heavyweight deps (d3, framer-motion, hls.js,
@spotify/basic-pitch, the Radix primitives, …) ship as regular
dependencies — npm/yarn installs them transitively.
The DAW does not add @kano/stem-web-audio-player to its peer
deps: the engine internals the DAW needs are bundled into this
package's dist/. Consumers that also use the engine directly (e.g.
stem-player-rd's session view) install it separately; the two
copies are intentional and isolated.
Exports map
Sub-path imports match the layout stem-player-rd consumes:
import { DAWView } from '@kano/stem-daw/components/DAWView';
import { DAWController } from '@kano/stem-daw/engine/daw-controller';
import { fetchTrackForDAW } from '@kano/stem-daw/services/track-search-api';
import { useDAWSessionStore } from '@kano/stem-daw/store/daw-session-store';
import { useDAWAuthStore } from '@kano/stem-daw/store/daw-auth-store';
import {
importStemFmMixConfig,
type StemFmMixConfig,
} from '@kano/stem-daw/engine/daw-import-stem-fm-config';
// Optional barrel — everything above plus extra primitives:
import { DAWView, DAWController } from '@kano/stem-daw';The full machine-readable map lives in package.json
under "exports".
Worklets — required runtime setup
DAWController boots four AudioWorklets that the browser fetches by
URL at start-up:
/workers/daw-stem-processor.js/workers/realtime-pitch-shift-processor.js(Rubberband)/workers/phase-vocoder3.js(mobile fallback)/workers/buffer-player-processor-202602.lavv8e32-ts.js
The package ships these files in dist/workers/. You must serve
them at the matching /workers/<name>.js paths or the DAW will fail
to initialise. Pick one of the two strategies below.
Option A — Vite plugin (recommended)
// vite.config.ts
import { defineConfig } from 'vite';
import { dawWorklets } from '@kano/stem-daw/vite';
export default defineConfig({
plugins: [dawWorklets()],
});What it does:
- Dev (
vite): middleware streams the worklet files out ofnode_modules/@kano/stem-daw/dist/workers/at/workers/<name>.js. - Build (
vite build): the same files are emitted into the consumer'sdist/workers/so the production bundle is self-contained.
The plugin never overwrites a same-named file already in the
consumer's public/workers/ — host app wins, the DAW only fills
gaps. Pass { publicPath: '/custom-workers' } if you serve them
under a different URL prefix.
Option B — Asset URL imports
If you'd rather drive the URL yourself (e.g. you bundle the DAW into a
non-Vite build), each worklet is reachable through the package's
./workers/* subpath export:
import dawProcessorUrl from '@kano/stem-daw/workers/daw-stem-processor.js?url';
import pitchProcessorUrl from '@kano/stem-daw/workers/realtime-pitch-shift-processor.js?url';
// …
// Then make sure your bundler emits those URLs at /workers/<name>.js,
// or override DAWController's worklet-path constants at startup.stem-player-rd currently uses Option A.
Local development against a consumer (yarn link)
Iterating on the DAW UI while running it inside stem-player-rd:
# in stem-studio (this repo)
yarn install
yarn build:lib
yarn link
# in stem-player-rd
yarn link @kano/stem-daw
yarn devyarn build:lib runs tsup + the worklet copy step (see
scripts/copy-daw-workers.mjs). Re-run it after any change you want
to hot-pick up in the linked consumer.
You can also point a single consumer at a sibling working copy via
its own Vite alias instead of linking globally — that's how
stem-player-rd's vite.config.ts already wires the @studio
alias when the submodule is present.
Internal labs (Beat Lab, pitch / autotune)
These features are off in the default @kano/stem-daw build (no Beat
Lab, no Autotune Lab, no pitch inspector, no key badge in the transport,
no beat-grid nudge). They are not shipped in dist/workers/analysis-worker.js
either.
Enable for local / internal builds only:
# library build (tsup)
DAW_INTERNAL_LABS=1 yarn build:lib
# standalone DAW app (vite)
VITE_DAW_INTERNAL_LABS=1 yarn dev
# password unlock in standalone (labs UI bundled, hidden until unlock)
VITE_DAW_INTERNAL_LABS_PASSWORD=your-secret yarn devPublish workflow
Releases run from .github/workflows/publish.yml
on tag push (v<major>.<minor>.<patch>). The workflow:
- Checks the tag version matches
package.json'sversion. yarn install --frozen-lockfile.yarn build:lib(tsup + worklet copy).- Sanity-checks every entry in the
exportsmap exists indist/. npm publish --access public --provenanceusingsecrets.NPM_TOKEN.
To cut a release:
yarn version --new-version 0.2.0 # bumps package.json + creates the tag
git push --follow-tagsManual dispatch is also supported via the Run workflow button (useful for re-publishing the same version after a build infra fix).
Verifying a build before publishing
yarn build:lib
npm pack # produces kano-stem-daw-<version>.tgz
# in a fresh vite app:
yarn add ../path/to/kano-stem-daw-0.1.0.tgz
yarn add react@^19 react-dom@^19 react-router-dom@^7 zustand@^5Then mount <DAWView />, confirm the worklets resolve from
/workers/*, and that import('@kano/stem-daw/engine/daw-controller')
yields a typed DAWController class.
Non-goal
This package does not absorb the audio engine. @kano/stem-web-audio-player
stays a separate package with its own publish pipeline. Reasons:
- The engine is reusable headless (e.g.
stem-player-rd's session view streams without ever mounting a DAW). - The DAW is React UI that depends on a specific stack (Radix, framer, etc.) that engine consumers don't want pulled in.
Keep them separate.
