Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Patternflow is a device, a firmware, a website and a pile of documentation, and
| A fix to the firmware **core** (`patternflow.ino`, `src/`) | the file itself — for a bug or a real improvement to the device, never to make room for a feature | | the same, and the change must still be right on a build that has no features at all |
| An enclosure or mechanical **remix** | `hardware/case/remixes/<maker>-<variant>/`, with a README that states Author, License, what it is based on, which board it fits, and the print or cut settings | one new folder; the official folders stay as they are | the README fields are checked |
| A **bridge** to host software (TouchDesigner, Node-RED, Max, a DAW…) | `integrations/<host>/` with a README, plus a row in `integrations/README.md`. It talks to the panel through the contracts in `docs/` and never imports firmware or web code | one new folder | the README has to name the contract it is built against |
| A change to a **wire contract** | [`docs/rest-api.md`](docs/rest-api.md), [`osc-spec.md`](docs/osc-spec.md), [`midi-spec.md`](docs/midi-spec.md), [`mqtt-spec.md`](docs/mqtt-spec.md), [`audio-ws-spec.md`](docs/audio-ws-spec.md), [`pfst-v2-spec.md`](docs/pfst-v2-spec.md) | the spec: bump its version line and add a Version history row | `docs-links` |
| A change to a **wire contract** | [`docs/rest-api.md`](docs/rest-api.md), [`osc-spec.md`](docs/osc-spec.md), [`midi-spec.md`](docs/midi-spec.md), [`mqtt-spec.md`](docs/mqtt-spec.md), [`audio-ws-spec.md`](docs/audio-ws-spec.md), [`pfst-v2-spec.md`](docs/pfst-v2-spec.md) | the spec: bump its version line and add a Version history row. A vector that quotes a sentence you change changes with it, in the folder beside the spec ([`contract-vectors.md`](docs/contract-vectors.md)) | `docs-links`, and `drift`, which replays every vector against the [virtual panel](tools/virtual-panel/README.md) |
| Web app work | `web/` — start at [`web/README.md`](web/README.md) and [`web/ARCHITECTURE.md`](web/ARCHITECTURE.md) | | `web-ci`: lint, typecheck, `check:ci` (the smoke suites and the drift checks), `next build` |
| A **pattern** | **not this repository.** Make it in the [Pattern Lab](https://patternflow.work/pattern-lab), press **Publish**, and it is on the [Community](https://community.patternflow.work/community) for every Patternflow to play, under the license you choose. [PATTERN_GUIDE.md](PATTERN_GUIDE.md) walks the loop. The presets bundled in the firmware and the Basics pack are curated by the maintainer from the repository's own sources | | |

Expand Down
11 changes: 6 additions & 5 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ What is here, and what is not. The rule for the split: **the repository root kee

## Contracts — other software is built against these, not against the firmware source

- [`rest-api.md`](rest-api.md) — the device's HTTP API (`/api/*`), the console pages, and the table for choosing between HTTP, OSC, MIDI and MQTT
- [`osc-spec.md`](osc-spec.md) — OSC over UDP (DAWs, Max, TouchDesigner)
- [`midi-spec.md`](midi-spec.md) — the panel as a network MIDI port
- [`mqtt-spec.md`](mqtt-spec.md) — MQTT topics, both directions, the roles, and why the channel decides whether a write sticks
- [`audio-ws-spec.md`](audio-ws-spec.md) — the audio-react WebSocket the browser extension and the phone app speak
- [`rest-api.md`](rest-api.md) — the device's HTTP API (`/api/*`), the console pages, and the table for choosing between HTTP, OSC, MIDI and MQTT, with test vectors in [`rest-api-vectors/`](rest-api-vectors/)
- [`osc-spec.md`](osc-spec.md) — OSC over UDP (DAWs, Max, TouchDesigner), with test vectors in [`osc-vectors/`](osc-vectors/)
- [`midi-spec.md`](midi-spec.md) — the panel as a network MIDI port, with test vectors in [`midi-vectors/`](midi-vectors/) and those for `/api/midi` in `rest-api-vectors/`
- [`mqtt-spec.md`](mqtt-spec.md) — MQTT topics, both directions, the roles, and why the channel decides whether a write sticks, with test vectors in [`mqtt-vectors/`](mqtt-vectors/) and those for `/api/mqtt` in `rest-api-vectors/`
- [`audio-ws-spec.md`](audio-ws-spec.md) — the audio-react WebSocket the browser extension and the phone app speak, with test vectors in [`audio-ws-vectors/`](audio-ws-vectors/) and those for `/api/audio` in `rest-api-vectors/`
- [`pfst-v2-spec.md`](pfst-v2-spec.md) — the `.pfs` show table, with test vectors in [`pfst-v2-vectors/`](pfst-v2-vectors/)
- [`contract-vectors.md`](contract-vectors.md) — how a vector of the first five is written, and how `npm run check:rest-vectors` and the four like it replay them against the [virtual panel](../tools/virtual-panel/README.md), a Patternflow with no board
- [`panel-compatibility.md`](panel-compatibility.md) — a buying guide first (which HUB75 panels light up and which stay dark), then the reference for other sizes

## How the firmware is put together
Expand Down
6 changes: 3 additions & 3 deletions docs/REPOSITORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ A map for people. (AI coding agents get the same map with more rules in [`../AGE
| `hardware/` | `pcb/` (KiCad source, Gerbers per revision, schematic PDF), `case/` (STLs by printer bed size, knob plates, the Blender source, legacy v2 cases, the old v1/v2 laser-cut drawings in `legacy_lasercut/`, and `remixes/` — enclosures other people designed and built, one folder each), `bom/` (the CSV that is the BOM source of truth) | [`hardware/README.md`](../hardware/README.md) |
| `web/` | The Next.js site at patternflow.work: landing page, the interactive guide (`/guide`, with `/guide/build`, `/guide/play`, `/guide/make` and `/guide/audio`), Live Editor, Pattern Lab, the community (only on the community host), the browser flasher (the **Flash Patternflow** button on the Pattern tab and in the Play guide; the images it installs are `public/flash/`), edition shelf, feature catalogue, device update handoff, journal, roadmap. The JS presets in `src/lib/presets/` are the source of truth for the firmware's preset headers. The breadboard build guide is a React page here, `src/app/build/breadboard/` | [`web/README.md`](../web/README.md), then [`web/ARCHITECTURE.md`](../web/ARCHITECTURE.md) |
| `shared/` | `constants.json`, the values the firmware and the site both carry: the panel geometries, the knob count, the encoder's 24 detents a turn, the bus maximum and the button timings. `npm run gen:shared` in `web/` writes `firmware/patternflow/src/core_shared.h` and `web/src/lib/shared/constants.ts` from it. Neither is edited by hand, and `check:shared` fails while either is stale. The folder exists because the lab counted 20 detents a turn, against the encoder's 24, until 31 July 2026 | [`constants.json`](../shared/constants.json) |
| `docs/` | Contracts other software is built against, how the firmware is put together, walk-throughs, the audio guide (`AUDIO_GUIDE.md`), the assembly map, the legacy v2.x build guide (`legacy/`), the writing voice (`voice.md` and `voice-banned.txt`), project records (`releases/`, one file per release) and media | [`README.md`](README.md) |
| `tools/` | Clients Patternflow ships that run on their own: the audio-react browser extension (also the authoring source of the device's `/audio-in` page), the Android capture app, an RTP-MIDI probe. Also `case-models/`, the scripts that make the /build page's 3D model of each case, the official one and every remix on its case switch | [`tools/README.md`](../tools/README.md) |
| `docs/` | Contracts other software is built against, each with a folder of test vectors beside it, how the firmware is put together, walk-throughs, the audio guide (`AUDIO_GUIDE.md`), the assembly map, the legacy v2.x build guide (`legacy/`), the writing voice (`voice.md` and `voice-banned.txt`), project records (`releases/`, one file per release) and media | [`README.md`](README.md) |
| `tools/` | Clients Patternflow ships that run on their own: the audio-react browser extension (also the authoring source of the device's `/audio-in` page), the Android capture app, an RTP-MIDI probe. Also `case-models/`, the scripts that make the /build page's 3D model of each case, the official one and every remix on its case switch. `virtual-panel/` is the other end of the contracts: a Patternflow with no board, on the firmware's ports, that the vectors in `docs/` are replayed against | [`tools/README.md`](../tools/README.md) |
| `integrations/` | Bridges that run inside or beside someone else's software (Ableton / Max for Live today) and talk to the panel only through the contracts in `docs/` | [`integrations/README.md`](../integrations/README.md) |
| `.github/` | Issue and pull-request templates (including the guide's **Stuck here?** form and **Share your build**), `SUPPORT.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, the CI workflows (web, every firmware composition, the core-names-no-feature checks, doc links, the drift checks, community folders carrying their README, console-page sync, firmware release assets, Discord dev-log) and their helper scripts | the header comment of each workflow |
| root | `README.md`, the three guides (`BUILD_GUIDE.md`, `PATTERN_GUIDE.md`, `FEATURE_GUIDE.md`), `CHANGELOG.md`, `CONTRIBUTING.md`, `CITATION.cff`, the two license texts and the `LICENSE` pointer, `AGENTS.md` (+ `CLAUDE.md`, a one-line shim) | |
Expand All @@ -26,7 +26,7 @@ The rule for where a document lives: **the root keeps three guides and no others
| Use the one you built | [patternflow.work/guide/play](https://patternflow.work/guide/play) — flashing, the knobs, installing patterns and the console, on a Patternflow you can turn. The first flash goes through download mode (hold BOOT, tap RST, let go of BOOT), and the Wi-Fi step usually does not appear the first time: the [Flash chapter](https://patternflow.work/guide/play#flash) and [`BUILD_GUIDE.md` §8](../BUILD_GUIDE.md#8-firmware) say what to do, in order |
| Make a pattern | [`PATTERN_GUIDE.md`](../PATTERN_GUIDE.md) — the Pattern Lab, the community, and the device. No local toolchain involved. The same loop as a tutorial: [patternflow.work/guide/make](https://patternflow.work/guide/make). [`firmware/CUSTOM_PATTERNS.md`](../firmware/CUSTOM_PATTERNS.md) is the older, more hands-on route |
| Write a firmware feature or cut an edition | [`FEATURE_GUIDE.md`](../FEATURE_GUIDE.md) → [`EDITIONS.md`](EDITIONS.md) → [`features/README.md`](../firmware/patternflow/features/README.md) → [`bundles/README.md`](../firmware/bundles/README.md) |
| Control the panel from other software | [`rest-api.md`](rest-api.md) has the table for choosing between HTTP, OSC, MIDI and MQTT; then the spec for the one you chose |
| Control the panel from other software | [`rest-api.md`](rest-api.md) has the table for choosing between HTTP, OSC, MIDI and MQTT; then the spec for the one you chose. [`tools/virtual-panel/`](../tools/virtual-panel/README.md) answers on the same ports with no board behind it |
| Put sound into it | [`AUDIO_GUIDE.md`](AUDIO_GUIDE.md). The browser tab, the microphone and MIDI with a DAW, step by step: [patternflow.work/guide/audio](https://patternflow.work/guide/audio) |
| Work on the site | [`web/README.md`](../web/README.md) → [`web/ARCHITECTURE.md`](../web/ARCHITECTURE.md) |
| Contribute anything | [`CONTRIBUTING.md`](../CONTRIBUTING.md) — one table of where each kind of contribution goes |
Expand Down
3 changes: 2 additions & 1 deletion tools/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# tools/

Clients that Patternflow ships and that run on their own — a browser extension, a phone app, a diagnostic script. The line between this folder and [`integrations/`](../integrations/README.md): a **tool** is a standalone program of ours; an **integration** is a bridge that runs inside or beside someone else's software (a DAW, Max, TouchDesigner, Node-RED). Both talk to the panel only through the contracts in [`docs/`](../docs/README.md#contracts--other-software-is-built-against-these-not-against-the-firmware-source).
Clients that Patternflow ships and that run on their own — a browser extension, a phone app, a diagnostic script. The line between this folder and [`integrations/`](../integrations/README.md): a **tool** is a standalone program of ours; an **integration** is a bridge that runs inside or beside someone else's software (a DAW, Max, TouchDesigner, Node-RED). Both talk to the panel only through the contracts in [`docs/`](../docs/README.md#contracts--other-software-is-built-against-these-not-against-the-firmware-source). `virtual-panel/` is the one folder here on the panel's side of them.

| Folder | What it is | Contract |
| :--- | :--- | :--- |
| [`patternflow-audio-extension/`](patternflow-audio-extension/README.md) | Chrome/Edge extension: captures the current tab's audio, splits it into four bands, one per knob, that you shape as boxes on the live spectrum, and streams the four knob values to the panel | [`audio-ws-spec.md`](../docs/audio-ws-spec.md) |
| [`patternflow-audio-android/`](patternflow-audio-android/README.md) | The phone-side twin: captures what the phone is playing and drives the knobs with it, for filming. It takes its mapping from the panel | [`audio-ws-spec.md`](../docs/audio-ws-spec.md), and the microphone endpoints of [`rest-api.md`](../docs/rest-api.md#microphone-audio-edition) |
| [`rtpmidi-probe/`](rtpmidi-probe/README.md) | A plain-Python RTP-MIDI session initiator that walks the MIDI contract against a panel from a machine with no MIDI driver | [`midi-spec.md`](../docs/midi-spec.md) |
| [`case-models/`](case-models/README.md) | Not a client of the panel: the scripts that make the 3D model of each case the /build page's preview shows (`web/public/cases/<id>/model.glb`), from the case's own files in `hardware/case/`. Run by hand; the site's build does not run them | The node names `web/src/components/3d/caseModels.ts` finds in every model, listed in its README |
| [`virtual-panel/`](virtual-panel/README.md) | Not a client: a Patternflow with no board. The guide's `DeviceSim` answers on the firmware's ports, and the vectors beside each contract in `docs/` are replayed against it on every pull request. `npm run virtual-panel` in `web/` starts one | [`rest-api.md`](../docs/rest-api.md), [`osc-spec.md`](../docs/osc-spec.md), [`midi-spec.md`](../docs/midi-spec.md), [`mqtt-spec.md`](../docs/mqtt-spec.md) and [`audio-ws-spec.md`](../docs/audio-ws-spec.md), the device's side of each |

## The extension is also firmware source

Expand Down
17 changes: 16 additions & 1 deletion tools/virtual-panel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,19 @@ HTTP is on 80 here as on the device, and Linux keeps every port below 1024 from

`GET /frame.png` and everything under `/vp/` are the panel's own, and the firmware has neither. The PNG is the 128×64 frame as the LEDs would show it, brightness included. `/vp/turn?knob=2&detents=3` and `/vp/button?knob=4&down=1` are a hand, and `/vp/advance?ms=500` moves the manual clock. `/vp/state` says what the running pattern has been given since start, its knob deltas and button presses, with the name card over it and the first error it threw. Knobs count from 1, as in every contract.

A transport is a file in `transports/` whose default export is a `Transport` (`panel.ts`). `start()` attaches each one whose `feature` is `null`, and a feature's only when asked for it. Each gets a `Seam` with the `DeviceSim`, the panel's clock, the HTTP routes and its ports, a place for the fields it adds to `/api/status`, and events for a hand's turn, a button, a pattern change and sleep. Step 4's plan lists the transports to come ([k4 to k20](../../docs/investigations/2026-10-cleanup-step4-plan.md#the-prs)).
A transport is a file in `transports/` whose default export is a `Transport` (`panel.ts`). `start()` attaches each one whose `feature` is `null`, and a feature's only when asked for it. Each gets a `Seam` with the `DeviceSim`, the panel's clock, the HTTP routes and its ports, a place for the fields it adds to `/api/status`, and events for a hand's turn, a button, a pattern change and sleep. There are eight:

| Transport | Feature | What it answers | `--port` |
| :--- | :--- | :--- | :--- |
| `rest-state.ts` | core | Device state in [rest-api.md](../../docs/rest-api.md): `/api/status`, `/api/params`, `/api/sleep`, `/api/display`, `/api/knobs`, `DELETE /api/crash` and the favicon | `http`, 80 |
| `rest-patterns.ts` | core | The pattern list, select and a module's files, over the Basics pack as `basics.zip` carries it, and the volume's fields in `/api/status` | `http` |
| `mqtt.ts` | `mqtt` | [mqtt-spec.md](../../docs/mqtt-spec.md) as a broker's client: the roles, the channels, the topics and the eight-second snapshot. `GET /vp/mqtt` is the banner a `message` put up | none, it dials the broker `/api/mqtt` names |
| `mqtt-http.ts` | `mqtt` | `/api/mqtt`, the device page's `/api/mqtt/config` and `/api/mqtt/forget`, and the three `mqtt` fields in `/api/status`. It has no Director, so `/api/mqtt/director`, `/flowlocal` and `/mode` are the 404 | `http` |
| `osc.ts` | `osc` | [osc-spec.md](../../docs/osc-spec.md) both ways | `osc`, 9001, and replies to `oscReply`, 9000 |
| `midi.ts` | `midi` | The RTP-MIDI session in [midi-spec.md](../../docs/midi-spec.md) and its *Host → device* table | `midi`, 5004, and the next port for data |
| `midi-out.ts` | `midi` | *Device → host* on `midi.ts`'s session, `/api/midi` and the `midi` block in `/api/status` | `http` |
| `audio-ws.ts` | `audio` | The WebSocket in [audio-ws-spec.md](../../docs/audio-ws-spec.md), its 500 ms release and `/api/audio` | `ws`, 81 |

Each file's header comment says which lines of the spec it follows, and which firmware source it reads where the spec says nothing. What none of them does is in each vector folder's README, under the vectors it does not have.

Nothing in CI runs `main.ts`. The suites in `web/scripts/drift/` call `start()` themselves: a vector suite gives each vector a fresh panel on free ports and the manual clock, and `check:virtual-panel` gives each preset in `firmware/patternflow/presets/` one of its own for two seconds and fails when any of them throws.
Loading
Loading