- TypeScript 47.9%
- Vue 31.1%
- JavaScript 20.5%
- Dockerfile 0.3%
- CSS 0.2%
|
All checks were successful
Build & Push / build-push (push) Successful in 1m37s
A browser editor for the Casio Privia PX-330, talking to the instrument over Web MIDI with Casio's Individual Parameter Transfer messages. - Protocol layer (app/lib/midi/casio.ts) and the Web MIDI connection, with per-parameter throttling, requests with timeout and retry, and a wake-up request that works around the instrument dropping the first messages after its port is opened. - A 2796-parameter registry covering the master, effect, part, tone, DSP and scale tune tables, plus the 250-tone list extracted from the manual. - Setup mode (Layer & Split, keyboard parts, effects, tuning) and Mixer mode (accompaniment and MIDI parts), with a local library, combos and JSON export/import. - Tones are written through the panel's own Setup parameter, so the instrument loads them the way its buttons do and the display follows. - docs/: what the instrument exposes over MIDI, the document contract and how the three test layers are run. - Forgejo CI builds and pushes the image; compose.prod.yml runs it behind the shared reverse proxy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| app | ||
| docs | ||
| public/icons | ||
| tests/e2e | ||
| .dockerignore | ||
| .editorconfig | ||
| .env.example | ||
| .gitignore | ||
| compose.prod.yml | ||
| compose.yaml | ||
| Dockerfile | ||
| eslint.config.mjs | ||
| nuxt.config.ts | ||
| package.json | ||
| PLAN.md | ||
| README.md | ||
| tsconfig.json | ||
| yarn.lock | ||
PX-330 Editor
A self-hosted, browser-based editor for the Casio Privia PX-330 digital piano. It talks to the instrument over Web MIDI + Casio SysEx (Individual Parameter Request / Send) using the coordinates from Casio's official MIDI Implementation, and works offline as a PWA.
Stack: Nuxt 4 · Nuxt UI v4 (Pug templates) · Pinia · @vite-pwa/nuxt.
Status
Early development. What exists today:
- Research —
docs/research.mddocuments the PX-330's SysEx format, every parameter it exposes (master tuning, mixer, system chorus / reverb / acoustic resonance, brilliance, 48 parts, per-part tone / vibrato / LFO / DSP parameters, scale tune), the 48-part layout, tone numbering and what is not reachable over MIDI (registrations, rhythms, songs) — all verified against the hardware. - Protocol layer —
app/lib/midi/casio.tsbuilds and parses IPR / IPS messages (7-bit packing, arrays, names);app/composables/useMidi.tsowns the Web MIDI connection, throttled parameter sends, requests with timeout / retry and applies every incoming value to the stores. - Layer & Split — the three parts the keyboard plays in one card: pick the tone of Upper 1, Upper 2 (the layer) and Lower 1 (the split) from a searchable browser of the 250 tones, set each one's octave, level, pan and mute, and dose the layer against the main tone with a balance that keeps the pair's loudness. Copy or swap parts, save a combination and recall it in one click (only those parts are sent, so the effects and tuning stay put), and re-apply it after the panel has overwritten a tone. Tones are written through the panel's own parameter, so the piano loads them the way its buttons do and the display follows.
- Setup mode — the keyboard parts (Upper 1 / Upper 2 / Lower 1 / Harmonize): tone, enable, volume, pan, reverb / chorus / acoustic resonance sends, tuning, bend range, octave shift, level, touch sensitivity, vibrato and LFO offsets, and the tone's DSP effect with its parameters named per algorithm (Rotary, Drive Rotary, EQs, Enhancer, Distortion, Tremolo / Auto Pan, Early Reflection); system reverb, chorus, acoustic resonance, brilliance and master mixer; master tune / transpose and the two scale tune tables. Read from / send to the piano per scope, local library, JSON export / import, tracking of tone changes made on the panel.
- Mixer mode — the eight accompaniment parts and the 16 MIDI parts: tone, enable, volume, pan, reverb and chorus sends.
- App shell — MIDI connection badge, virtual keyboard (drives MIDI part C01 / C10), Drums and System placeholders.
Verified against the instrument: every parameter of both scopes reads back (287 + 1368 requests answered), a tone picked in the editor is loaded by the piano through the panel, and edits are applied live. Planned next: the Drums step sequencer with SMF export for the piano's Song mode, and the System page (device id, user song list).
Layer and Split are switched on and off from the panel — the piano exposes no
parameter for them, nor for the split point (see docs/research.md).
Requirements
- A Casio Privia PX-330 connected over USB (class-compliant, appears as
CASIO USB-MIDI; the piano must be in normal mode, not USB storage mode). - A browser with Web MIDI + SysEx support: Chrome / Chromium / Edge, or Firefox with the MIDI permission granted.
- A secure context:
http://localhostworks out of the box; when served from another host it must be over HTTPS.
Running
With Docker:
docker compose up
# http://localhost:7330 — set PORT in a .env file (see .env.example) to use another port
With a local Node.js (24+):
yarn
yarn dev # dev server at http://localhost:3000
yarn lint # eslint
yarn typecheck # vue-tsc
yarn build # production build (generates the PWA service worker/manifest)
The production build is a fully static SPA (ssr: false) — serve
.output/public from any static host or reverse proxy (HTTPS required for
Web MIDI outside localhost).
How it works
- Parameter registry (
app/lib/params/): every parameter is a declarativeParamDef(id, label, section, type, range / enum, default, display formatter and its SysEx coordinates: category, parameter set, block, parameter id, bit width, array index, bias). Components render parameters from their ids; the registry alone decides what is sent to the piano. - Tests:
docs/testing.md— unit tests of the pure modules, an end-to-end run against a fake instrument, and one against the real piano. - State (Pinia): one store per editor document, all built on
useParamState(registry), the single write funnel — every change goes throughsetParam(id, value), which coerces the value, persists it and, when MIDI is connected, emits the IPS. - MIDI (
app/lib/midi/casio.ts,app/composables/useMidi.ts): pure protocol builders / parsers on one side, the Web MIDI connection on the other. Values received from the piano (replies and spontaneous sends) are applied to the stores with an echo guard. - Persistence (
app/composables/usePatchStorage.ts): alllocalStorageaccess in one composable.
Reference documents
The SysEx coordinates, parameter ranges / enums and lists in this project were derived from Casio's official documentation:
- PX-330 / AP-620 MIDI Implementation (Casio, 2009)
- PX-330 User's Guide (tone list, drum assignment list, rhythm list)
The PDFs themselves are Casio's and are not redistributed in this repository
(they live in the git-ignored ref/ directory).
This is an independent project and is not affiliated with or endorsed by CASIO COMPUTER CO., LTD. Privia is a trademark of Casio.