Skip to content

Releases: engmung/Patternflow

Patternflow v3.11.0

Choose a tag to compare

@engmung engmung released this 04 Oct 14:39

Installed patterns run faster and come back at once. The console is light and one thing. The audio path is rebuilt from the extension to the microphone.

Patterns

  • Installed patterns run faster, and nobody has to edit one. Patterns are now compiled at -O2 with the maths they call most computed inline, and the results are bit-exact. The Basics pack is rebuilt this way for the first time since August: every one of its 33 patterns is faster, 1.11 to 2.36 times the frames a second, 1.52 at the median. On a second panel three of them went from 24.2, 18.5 and 33.0 ms a frame to 11.5, 11.0 and 12.8.
  • To get it: update the firmware first, then install the Basics pack again from the site. A pattern already on a panel keeps the build it was installed with. Community patterns are rebuilt the first time someone sends one again.
  • The rebuilt pack needs firmware 3.5.1 or later (3.2.0 to 3.4.0 refuse all 33 of its patterns), and 3.10.5 or later for the largest ones on the Audio edition. Update first; reinstalling replaces files that load with files an old firmware will not.
  • Going back to a pattern no longer loads it again. A pattern that has run stays parked in PSRAM, and picking it again resumes it in about 0.2 ms where the heavy ones took seconds (Branched Flow 3.3 s, Two Stream 4.1 s). Up to 16 stay parked; they give their memory back when a new load needs it.
  • SELECT shows parked patterns live as the knob reaches them.
  • What this takes away: a pattern that wants a fresh start every time it is picked does not get one. setup() runs once per load. Restarting the panel, or any install, delete or reorder on the Patterns page, starts it again.

The console

  • Light by default, with dark as a remembered toggle, and one look across its pages.
  • The home page is the panel's control surface: now playing with its arrows, on/sleep, brightness and the four knobs, with the device's facts under them. The edition has a card of its own, with the pages it carries and the update notice.
  • Knobs: set all four at once with the All row.
  • The Status page shows the crash record when there is one.

Audio edition v0.7.0

  • The Audio page is a console page. It says who has the knobs (the microphone listening, off or not fitted; the extension or the phone app), names the bands K1 to K4, and shows what each knob actually gets.
  • It saves only what you changed. It reads the panel's mapping before it can write, so a failed read followed by one edit can no longer overwrite four bands, and two tabs no longer undo each other. The page is 17.2 KB where it was 19.9.
  • A microphone that is not delivering no longer moves the knobs.
  • The browser extension (0.2.0) holds the knobs through silence. A paused or quiet tab keeps each knob at its rest value instead of letting go after half a second.
  • A knob set from the console's sliders, by MIDI CC 20-23 or by /api/params stays where it was put until its encoder is turned; sound does not move it, and the Audio page does not show that yet.
  • Live means the panel is being driven. Audio-React switched off on the panel, a silent tab, a closed tab, a wrong address and a panel without the Audio edition each say so, where every failure used to read "WebSocket error".
  • To update the extension, pull the repository and press Reload on chrome://extensions; your mapping is kept.

Updating

  • Standard firmware: the notice on the console's home page, or patternflow.work/update.
  • Audio: patternflow.work/editions, the Audio card, Install to my panel; or drop patternflow-audio.ino.bin from this release on the panel's Update page. The link on an older panel's Update page goes to /update, which installs the standard firmware and would replace the edition. Your settings stay, and the Audio card puts it back.
  • Reflashing keeps your patterns, your Wi-Fi and your settings.

Editions

Audio v0.7.0 is cut on this core. Performance v0.4.0 is still the image on the shelf and is on core 3.10.4 until its maintainer re-cuts it: no parked patterns, no light console, and a pattern's code still lives in internal RAM there, so the rebuilt Basics pack installs but a large community pattern can be refused. The clock and MIDI try-out images are unchanged.

On the site

  • An Audio guide joins Build, Play and Make at patternflow.work/guide: the edition, sound from a browser tab with the real editor to try, the on-board microphone, and the board as a MIDI port with Ableton.

How this was checked.

  • Pattern speed: measured on two panels by its author, and three patterns measured again on a third.
  • Parked patterns: measured on two panels with 12-13 modules parked.
  • The console and the Audio page: on panels, light and dark, phone and desktop widths.
  • The extension: run against a panel with the microphone. A tone moved K1, four seconds of silence left the knobs held, and Stop handed them back.
  • Updates over Wi-Fi: done repeatedly on three panels between Core and Audio images.
  • The Audio edition's Wi-Fi power (19.5 dBm, where it had been pinned at 17): the microphone's floor was read at both, alternating builds on one panel, and the two are indistinguishable.
  • Not checked: the Audio page on a real phone or in the phone app, and the extension in a Chrome that asks for local-network permission. The Performance and MIDI images build and pass their checks but were not run on a panel for this release.

The full list is in CHANGELOG.md.

Patternflow v3.10.5

Choose a tag to compare

@engmung engmung released this 01 Oct 23:26

Every pattern runs faster, patterns that were refused for size now load, and the panel raises its own Wi-Fi when it has none.

Patterns

  • Every pattern is 2.45 ms a frame faster. Copying a finished frame to the panel was the largest fixed cost in the loop: 5.96 ms of every frame, nearly half of Origin's. It is 3.50 ms now and the picture is the same, word for word. Origin goes from 12.1 to 9.6 ms a frame, a 30 ms pattern to 27.6.
  • A pattern's code no longer takes the memory the network runs on. A loaded pattern's code had to fit in the board's internal RAM, the same RAM the console and Wi-Fi live in. It now runs from PSRAM, which has megabytes to spare. On the Audio edition a pattern with 23 or 32 KB of code used to be refused and now loads; one community pattern was failing to load about one time in four for want of 528 bytes and now loads every time. With a pattern running, the console has about 28 KB of internal memory to work in whatever the pattern's size, where a large one used to leave 21-23. Frame times are within about one percent of what they were.
  • A pattern's global constructors can use the host. A pattern that allocated memory or asked for a random number in a namespace-scope initialiser crashed the panel every time it was picked. It runs now.

Nothing to rebuild: installed .pfm files are unchanged and get all of this from the firmware.

When something goes wrong

  • A panel that crashed says where. /api/status used to say panic and nothing else. It now carries a crash object: which pattern was running, which of its calls (setup, update, draw), and the address inside the pattern, as an offset the .pfm's own symbols resolve. Clear it with DELETE /api/crash. It only reports; nothing is skipped or restarted on its account.
  • A pattern that hangs no longer takes the console with it. A pattern stuck in draw() used to freeze the panel and then the console behind it, and the only way out was the power cable. The console now keeps answering, the Status page says the panel is not answering and for how long, and Reboot and /update still work.
  • Five requests that could reboot the panel cannot any more. A form upload cut off part-way, curl -X POST to /api/patterns or /update with no form in it, and a request naming an absurdly long multipart boundary each restarted the board; a quote in a pattern's name made the pattern list unreadable; an abandoned upload could leave storage stuck busy. The request parser is now replayed against 13,953 cut-off, stalled and malformed requests on every change.
  • Lanes keep working past day 25. A panel left running untouched would have stopped answering its microphone or its weather on day 25.

The panel's own network

  • With no known Wi-Fi in reach the panel raises a hotspot, patternflow-a1b2 (password patternflow until you change it), and the whole console is at http://192.168.4.1/ on it - patterns, knobs, updates, and the Wi-Fi page to add the network of wherever you are. It lists the networks it can see so you can tap one instead of typing it.
  • Switching console tabs no longer downloads the page again, and the header is the same on every page.
  • A firmware upload survives a Wi-Fi hitch of up to two minutes, and the radio is back at full transmit power.

Also

  • Two 64x64 modules chained make a 128x64 panel (PANEL_GEOMETRY, the firmware64x2 build).
  • No more stray letters on the panel's own screens, and a deck sent from the community's dock keeps its order.

Editions

Audio v0.6.5 is re-cut on this core. Performance v0.4.0 - the clock, a Weather face and room for 100 sequences - is the image on the shelf and gets this core when its maintainer re-cuts it. Reflashing keeps your patterns, your Wi-Fi and your settings.

On the site

  • A guide you can play along with, in three parts, at patternflow.work/guide - work in progress and marked so.
  • A moderator can take a pattern or deck off the community wall without deleting it, and one press of Publish is one post.

How this was checked. The memory, speed, crash-record and hung-pattern changes were measured or reproduced on two panels - the default image and the Audio edition - before they were merged, with switch soaks of 160 to 400 pattern changes and no reset. The Performance and MIDI images build and pass their checks but were not run on a panel for this release, and none of it has had days in a closed enclosure yet. If something misbehaves, /api/status now has more to say about it: please attach it to the issue.

The full list is in CHANGELOG.md.

Patternflow v3.10.4

Choose a tag to compare

@engmung engmung released this 19 Sep 03:13

The panel films properly on a phone.

Panel

  • It refreshes at exactly 300 Hz now, and that is a number chosen for cameras. Each row of the panel is lit as a short pulse once per refresh. A phone's rolling shutter collects a whole number of those pulses on each line of its sensor, and unless the exposure is an exact multiple of the refresh period, some lines get one pulse more than others — the rolling bands you see on video. Phones do not pick exposure times freely: to avoid mains flicker they use 1/50, 1/100 and 1/25 in 50 Hz countries, 1/60 and 1/30 in 60 Hz ones. 300 Hz is the lowest rate all of those divide, so at those shutter speeds every line gets the same number of pulses and the bands are gone.
  • 3.10.3 had made this worse. Its 325 Hz happened to land close to the worst case for both 1/50 and 1/60 — more lines on video than before. Faster is not what a camera needs; exact is.
  • It costs about 8% of the light and nothing else: the refresh runs on the chip's DMA hardware, and frame times on the panel are unchanged.

What it cannot fix: shutter speeds of 1/120 and faster, and slow motion. Covering 1/120 would take 600 Hz, which this panel clock cannot reach at full colour depth.

When you film: lock the shutter at 1/60 (or 1/50) with a manual camera app or pro mode. If you cannot, turn the panel down — a dimmer panel makes the phone lengthen its exposure, which is what you want. Lock white balance and focus, and switch HDR and night modes off. The firmware README has the full explanation.

Nothing to set, and every installed pattern gets it without a rebuild.

Editions

Audio v0.6.4 and Performance v0.2.9 are re-cut on this core. Reflashing keeps your patterns, your Wi-Fi and your settings.

Patternflow v3.10.3

Choose a tag to compare

@engmung engmung released this 17 Sep 08:23

The panel's colours come out right. If bright edges on your panel have been growing purple and sky-blue fringes, or greys have looked pink, this is the fix — and part of what it fixes is 3.10.1.

Panel

  • No more purple and sky-blue fringes round bright edges. On a pattern with a near-white background, every edge that faded to dark grew a purple outline and then a sky-blue one. The cause was in the display driver: a bit plane stays lit while the next one is being sent, so each brightness window was being sized for the wrong plane. 3.10.1's dark-end fix had read that as a bug in the lowest plane and "corrected" it, which made one step in the middle of the range 42% darker than the step below it. Red, green and blue reach that step at different points along a fade, and that is where the colours came from.
  • The bit planes are exactly binary now, at every brightness. Getting there meant changing the order the planes are sent in, not just the windows: the order the driver came with cannot be made exact without throwing more than half the light away.
  • 325 Hz instead of 260. The new order is shorter, so the panel refreshes faster — less banding when you film it. Nothing to set.
  • Greys are grey. The white balance that shipped since August (red 0.93, blue 0.975) had been tuned by eye on top of the old, uneven planes — it was correcting the driver, not the panel. It is back to neutral. If you set your own LED_WB_* in patternflow_secrets.h, look at it again on the grey test card (/panel-tuner.html?device=<your panel's address> on a local dev server connects by itself).
  • It is a little dimmer at full brightness — about 83% of what the old driver put out, because the old driver was brighter by being wrong. The middle tones are where they were meant to be.

Because all of this is in the driver, every installed pattern gets it without a rebuild.

Still to do: the saturation boost (1.62) was tuned the same way as the white balance and has not been looked at again, and the power clamp's full-white figure was measured on the old driver, so it now steps in a little earlier than it needs to on a white-out frame. That errs on the safe side and stays until it is measured.

Firmware core

  • Knobs page. /knobs on the device, GET/POST /api/knobs behind it: which way each encoder counts and how many edges make a click, per knob, with a live readout — instead of INVERT_ENCODER and a rebuild.
  • Storage errors say why. A failed upload reports errno, so a missing folder reads differently from a volume that will not write. From RayWolters' investigation on #424.

Web

  • Features — every firmware feature as one row: what it is, which firmware carries it, a reel of it running. The clock and the USB-MIDI build, which are not on the shelf, can be installed from their rows to try.

Hardware

  • The v3.9 board is the current board everywhere in the docs: the v3.0 board with the USB-C footprint removed. J4, the screw terminal, is the only power input. Same case, same pin map, same guide.

Editions

Audio v0.6.3 and Performance v0.2.8 are re-cut on this core. Reflashing keeps your patterns, your Wi-Fi and your settings.

Patternflow v3.10.2

Choose a tag to compare

@engmung engmung released this 11 Sep 02:12

Pattern storage that will not mount now says why, and a Format that did not take no longer says it did.

Pattern storage

  • A Format that did not take no longer reports success. The format never read back what it wrote, and on a board whose flash silently drops writes, every step returned OK. The console said "storage ready", and the next mount failed exactly like an unformatted board (#424). The mount that follows a Format now decides the result. If it fails, Format fails and tells you why.
  • A failed mount says why. The serial line used to be Mounting FFat partition failed! Error: -1 whatever the cause. The first failure in a boot now reads the storage's boot sector and records what it found: not formatted (a freshly erased board, so press Format), format did not stick: storage reads back blank, or the bytes of a sector that is not a filesystem. It shows on serial and as fsError in /api/status.
  • /api/status gains flashId, the flash chip's JEDEC id.

Nothing changes on a board whose storage mounts.

Editions

Audio v0.6.2, Performance v0.2.7 and Clock v0.1.5 are re-cut on this core. Reflashing keeps your patterns, your Wi-Fi and your settings.

Patternflow v3.10.1

Choose a tag to compare

@engmung engmung released this 10 Sep 15:04

The panel's darkest tones come out right, and a handful of things that could go wrong on a real panel no longer do.

Panel

  • A higher level no longer gives less light. Near black, raising a code dimmed the panel in 26 places — twice by half. Fixed in the display driver, so every pattern gets it, including ones already installed.
  • Dark greys stop looking tinted. Neutral greys near black came out blue, cyan or red. Tinted levels went from 44 to 6, and the darkest levels that lit nothing at all from 9 to 5.

Firmware core

  • An upload that stalled for a few seconds could reboot the board.
  • A failed settings write could make the next boot forget a good pattern. /api/status now reports nvs.failures.
  • Re-uploading a pattern under the same name now updates its picture.
  • A long frame — installing a pattern, saving settings — no longer makes animation jump.

Audio · MIDI

  • An Audio panel with the mic switched on but nothing wired no longer takes over the knobs.
  • A backlogged microphone no longer colours the lanes for seconds.
  • All Notes Off and All Sound Off now release a stuck MIDI note, and a stuck note no longer disables the panel's own button.
  • Automation moves the same distance whatever the frame rate. POST /api/params gains rN=1 to release a channel.

For pattern authors

PFColor::sampleRampLerp is new for gradients; sampleRamp stays posterised. hsvToRgb rounds, cellHash's seed now actually decorrelates, and PFMath gains ifloor, floorF, clamp and clamp01 — floorf and fminf are function calls on this chip. These reach a module when it is next built; installed .pfm files are unchanged.

Editions

Audio v0.6.1, Performance v0.2.6 and Clock v0.1.4 are re-cut on this core. Reflashing keeps your patterns, your Wi-Fi and your settings.

Patternflow v3.10.0

Choose a tag to compare

@engmung engmung released this 08 Sep 08:04

A heavy pattern comes on every time now. That is the one to reflash for.

Firmware core

  • A pattern no longer gets refused while the memory it needed is free. If a
    big pattern sometimes just didn't appear — dark panel, no error, a different
    one each reboot — this was it.
  • Pushing a frame to the panel is about 14% cheaper. Origin goes from 72.5
    to 78.2 fps.
  • Thumbnail work and installing or deleting a pattern left the render loop. An
    upload that held the panel still for two seconds now holds it for about fifty
    milliseconds.
  • Wi-Fi gets a moment to recover on the network it was using before it starts
    cycling through your saved ones. POST /api/wifi/reconnect reconnects without
    a reboot.

Audio · MIDI

Network MIDI runs as its own service, with queues that no longer wait on a heavy
pattern's next frame. Incoming automation is no longer echoed back out as
controller messages.

For pattern authors

api->alloc() is a zeroed allocation again, as the ABI has always documented —
it had briefly stopped clearing what it handed out. If your pattern looked wrong
on its first frames recently, this was probably why. The ABI is unchanged; your
existing .pfm files keep working.

Editions

Audio v0.6.0, Performance v0.2.5 and Clock v0.1.3 are all re-cut on this core.
Reflashing keeps your patterns, your Wi-Fi and your settings.

Patternflow v3.9.5

Choose a tag to compare

@engmung engmung released this 06 Sep 09:58

A firmware release, and a real one: every edition gets the same core. The reason to reflash is the web console — it survives a slow Wi-Fi link now, a heavy pattern can no longer take it down, and the panel's own screens stopped fighting each other.

What this release is

The console survives a slow Wi-Fi link. A panel whose link was moving 2–5 KB/s got every console page cut short: the page sender's hard 5-second budget — written for the days when a send ran inside loop() — broke off with the header already promising the full length, and the browser showed a page whose script ends mid-word. The sender now gives up only when the peer stops accepting bytes, and pages go out gzip-compressed (a third of the bytes; the 63 KB audio page is 17.6 KB on the air). The same link exposed a reboot: the vendored WebServer parsed requests through a spin loop, harmless on Core 1, fatal on Core 0 where the task watchdog lives — a request that stalled for five seconds took the board down. It waits instead.

A big pattern can no longer take the console down with it. The loader put a module's data in internal RAM whenever it fit, and internal RAM is what Wi-Fi and every HTTP connection are made of; a community pattern with 46 KB of tables left 2.7 KB, and the panel drew on while nothing answered. Data over 16 KB, or anything that would leave less than 24 KB, goes to PSRAM now. Measured: no change to the frame time, ~19 % on the heaviest setup().

Browsing with K4 shows thumbnails, and a pattern loads on the other core. SELECT used to load every module the knob passed over — seconds of frozen panel per heavy pattern, and the detents you turned meanwhile applied all at once. The knob now moves only the highlight (three detents per pattern); the panel shows each pattern's thumbnail, the frame it last drew; and when the knob rests, the load runs on Core 0 while the frame keeps drawing. The same path serves switches from the console, OSC, MIDI and a show; /api/status says loading while one is on its way.

Panel screens. The UPDATE card no longer tears against the pattern while an image is received (the upload handler moved cores in 3.9.1 and kept drawing from there). The name that appears when a pattern is chosen from the console reads upright. The console home page gains a brightness slider beside the panel switch (GET /api/display?brightness=), and /api/status reports brightness and resetReason — a board that rebooted itself now says so.

Audio edition. The microphone's lane motion no longer goes out as MIDI or OSC knob events (it never was a hand, and in a quiet room it was 26–43 messages a second filling the Wi-Fi transmit queue); MIDI knob output is paced to one message per knob per 50 ms. Specs: REST 1.4, MIDI 1.1, OSC 1.1.

Clock edition (Community shelf): one layout, seven faces, the pattern inside the digits — cut earlier on its own line, and on this core now.

The full list is in CHANGELOG.md.

Installing

From the console: the update banner offers this build, or /update takes the image for your edition. A new board takes the four core images below with the offsets in FLASHING.md, or the browser flasher at https://patternflow.work/flash. Editions: Audio v0.5.4, Performance v0.2.4, Clock v0.1.2 — one click each on https://patternflow.work/editions. Patterns, networks and settings are kept; the thumbnails build themselves the first time you browse.

Patternflow v3.9.4 — one pattern runtime, releases in two commands

Choose a tag to compare

@engmung engmung released this 04 Sep 01:43

A web and tooling release. The panel firmware is 3.9.3's — the core image is rebuilt only to carry the version, so there is nothing to reflash for; the update banner on the console can be ignored this time.

What this release is

One pattern runtime. The community's sandbox page — the XSS boundary every preview and thumbnail renders inside — was a hand-kept plain-JS copy of the lab's runtime, with "keep the two in sync" notes on both sides. It is now built from the lab's own code (scripts/build-sandbox.ts bundles src/sandbox/sandbox.ts with lib/pattern/harness.ts, ramp.ts and matrix.ts), and check:sandbox-sync in CI fails when the committed page is not that build. Two drifts came out in the wash: the sandbox turned a knob 20 detents a revolution where the lab and the encoder turn 24, and a pattern that threw reported its message without the line the lab quotes. Everything else was compared pixel for pixel against the old page before it was replaced.

A release is two commands. python firmware/toolchain/release.py cut vX.Y.Z [--audio vA] [--performance vB] bumps every version site, dates the changelog, runs shelf.sh for each image, points the flasher manifest and the /editions cards at them, runs the web checks, commits and tags; release.py publish vX.Y.Z --notes FILE pushes, opens and merges the dev → main pull request, and creates the GitHub release with the edition images attached. check_versions.py (in CI) reads the six files a version lives in and the shelf, and fails on any disagreement. This release is the first one cut that way.

Firmware compiled in CI. firmware/bundles/build.sh all — default, audio, performance, and the marker scan — runs on every pull request that touches firmware/. (Shipped in 3.9.3's tree; the two fixes it needed on a fresh runner — the scripts' executable bit and cloning the vendored libraries before PlatformIO resolves them — are in this one.)

The community host, written down. docs/SERVICES.md now records the build worker's sandbox: a systemd drop-in (no network, read-only system, three writable paths, limits), the worker's secrets kept outside the unit, and the compiler itself run under bwrap with only the toolchain, the firmware headers and the build directories in view — plus the host update procedure and the one small exposure that remains.

The full list is in CHANGELOG.md.

Installing

Nothing to do. If you are on 3.9.3 you have this firmware already. A new board still takes the four images below with the offsets in FLASHING.md, or the browser flasher at https://patternflow.work/flash. The Audio (v0.5.3) and Performance (v0.2.3) edition images are unchanged and attached for completeness.

Patternflow v3.9.3 — the list rebuild, jsMod, firmware in CI

Choose a tag to compare

@engmung engmung released this 03 Sep 12:58

The list rebuild that stopped the render for two seconds is gone, a JavaScript % no longer costs a library call per pixel in a module, and the firmware is compiled in CI. The picture on the panel is unchanged; the times are not.

What this release is

Rebuilding the pattern list no longer holds the frame. After an install batch, and on deleting the module that is drawing, the registry rescanned /patterns and opened every module's sidecar twice — with forty modules, 2.4 seconds with the render stopped, which 3.9.1's loopSyncMaxUs finally put a number on. The sidecar's two facts are now cached by path and forgotten only by the writer that changed them (upload, delete, format, the library pull), and the scan reads the directory with readdir instead of opening every entry to build a File. Deleting the drawing module answers in 0.29 s; the render pauses 55 ms where it paused 2,160. Boot still reads every sidecar, once.

PFMath::jsMod. A pattern ported from the lab reached for fmodf on every pixel for a hue wrap like (h + 0.33) % 1, and inside a module that is a call into the host's libm. jsMod(x, m) is one division and one truncation with JavaScript's sign rule. The 22 preset twins that used fmodf (52 calls) use it now, and the lab's C++ prompt tells the model to. 0510 rebuilt against it renders at 17.9 ms a frame where it rendered at 20.8.

The frame push writes two columns at a time. Adjacent columns share one aligned 32-bit word in every plane, so the blit does one read-modify-write per plane for two pixel pairs instead of two, the CIE table lives in DRAM, and the function in IRAM. Bit-exact, like 3.9.2's tables. 7.0 → 6.9 ms; what remains is the DMA engine reading the same memory while the CPU writes it.

Firmware compiled in CI. firmware/bundles/build.sh all — default, audio, performance, and the marker scan that proves each image carries exactly its features — now runs on every pull request that touches firmware/, with the PlatformIO packages cached. Until now the checks in CI were greps and the compiler ran at a desk.

Edition write-ups are releases too. The release workflow no longer fails on a tag that is not a core vX.Y.Z. The first is the Audio edition's own release (audio-v0.5.3), published alongside this one.

Verified on an Audio-edition panel: the cross-core regression from 3.9.1 (flash, upload while a module draws, step, delete the drawing module, the bus, flash again) passes on this build; the numbers above are from the same panel.

Editions

Audio v0.5.3 and Performance v0.2.3 are this core with no feature changes of their own.

Installing

Already running Patternflow: pick your edition on patternflow.work/editions — one click, over Wi-Fi. Or drop patternflow.ino.bin on http://patternflow.local/update.

A new board, or recovery over USB: all four images with offsets in FLASHING.md, or the browser flasher at https://patternflow.work/flash.