MoshBox
This is the operating manual for MoshBox. It is a live datamosh instrument: one video input, one moshed output, a small grammar of packet operations, and an eight-bank frame tape.
Its behaviour is informed by Interstream v2.0 by Jonas Bers / Signal Culture, but the implementation is new.
Signal flow
Camera or clip ──► encoder ──► mosh grammar ──► decoder ──► HDMI
▲ │
└── frame tape ◄─┘
The encoder and decoder form one continuous session. Sources are normalised to
the scene size before encoding, so switching between them does not reset the
codec. A source change is an event the grammar sees: the SourceSwitch op makes
it a clean cut or a smear that drags the previous reference into the new picture.
The frame tape sits alongside the output. It stores decoded pictures for time-domain repeat and, in Mosh mode, can feed them back through the encoder.
Setting up
You need:
- One USB UVC camera or capture device (the main source)
- An HDMI display, mixer or capture card
- A MIDI controller for hands-on control; the default map is for a Korg nanoKONTROL2
- A power supply suitable for your Raspberry Pi device (5V / 2.5 A for Pi 3 or Zero 2, 5.1 V / 3.0 A for Pi 4)
Connect the camera, HDMI and MIDI, then power up. If no camera is attached, MoshBox plays the Autumn Sun over the river Spree, so the engine still has something to work with.
The output resolution is baked into the image. The standard Raspberry Pi 3 build is 640×480, a 4:3 SD image for analogue chains. The Pi 4 build is 1280×720, a 16:9 HD Ready image. Capture, scene and display share one resolution, so the picture is not rescaled on the hot path.
Sources
The camera is the shipping source and hot-swaps: plug a capture device in after boot and MoshBox opens it. The clip is the fallback. It is the first video file by name in the data folder, looped, and it is the drop point for footage without a rebuild. There is no playlist and no USB watching.
sswitches to the next source. On the nanoKONTROL2, the TRACK buttons do this.cselects the camera.Ccycles capture devices.ljumps straight to the clip.- The SOURCE SWITCH pot sets what happens at a source change: 0 is smear, 1 is clean. The default is clean.
Sources are cover-cropped to the scene's shape before encoding, so a camera and a clip of different shapes arrive at the encoder as the same format.
Switching sources mid-mosh is the point of the single-stream design. With the SOURCE SWITCH pot toward smear, the new source inherits the old reference and the change melts; with clean, a keyframe is forced and the cut is hard.
The mosh grammar
MoshBox treats the encoded stream as a list of packets and runs a program of ops over it. A program is an ordered list of ops with parameters, a seed and a tempo.
| Group | Ops | What they do |
|---|---|---|
| Reference | DropKey, ForceKey, Normalize | Withhold keyframes, force a clean re-anchor, or flush the references |
| Repeat | RepeatDelta, Stutter, Bloom | Replay deltas so motion accumulates |
| Damage | Corrupt | Damage packet payloads without touching their headers |
| Order | Hold, Shuffle, SortByMotion, Reverse | Reorder packets inside the window |
| Rhythm | Pulse | Open and close the mosh on the beat |
| Source | SourceSwitch | Choose a clean cut or a cross-source smear at a source change |
The default program is SourceSwitch + DropKey + RepeatDelta. DropKey
withholds keyframes so the decoder predicts from a stale reference;
RepeatDelta replays delta packets so the motion keeps accumulating. That is
the continuous mosh. Press mosh (space, or SOLO 1 on the nanoKONTROL2) to engage
it.
The program runs a small state machine. Idle is a clean pass-through, Active is the mosh, and Recovering lets a clean reference through while repeat and damage ops are quiesced. That is why Normalize can snap the picture back to a clean reference.
Presets can carry other combinations of these ops. The default program is not the only program.
Mosh modes
MoshBox has a continuous mosh and two optional layers:
- Continuous — the default program. A steady pour with no clock.
- Rhythm — adds Pulse, which opens and closes the mosh on the beat. The PULSE
DIV and PULSE HOLD faders shape the gate. Toggle it with
por SOLO 3. - Motion — the source's own motion drives RepeatDelta.count, so the mosh
stretches harder when the picture moves. Toggle it with
mor SOLO 4; the MOTION AMOUNT and MOTION MAX pots set the depth and ceiling.
The layers can be combined. Continuous is the foundation; rhythm and motion are layers on top. Rhythm uses the clock: the tempo can come from the internal BPM, MIDI clock, or tap tempo. Presets capture the tempo.
Frame tape
The grammar's repeat ops work in the packet domain: they re-apply motion, so the picture stretches. The frame tape is the other kind of repeat. It stores decoded pictures, so it can play a moment again exactly — or feed that moment back through the current program.
The tape has eight banks. Each bank can hold a loop of 1 to 120 frames (4 seconds at 30 fps). It runs at 30 fps, independent of the render rate. A bank is captured from the live history; the default loop is the most recent 4 seconds.
Banks
- Press an empty bank (
1–8, or REC 1–8) to capture the last loop and start playing it. - Press a filled bank to recall it.
- Press the active bank again to toggle between the loop and live passthrough.
xcaptures into the active bank, overwriting it.kclears the active bank.jfreezes or resumes the playhead.ytoggles loop / live.icycles the loop mode: forward, ping-pong, reverse.,and.change the loop length.<and>move the loop window.- The MARKER RIGHT button restarts the active loop.
Wet, dry, clean and mosh
- Wet is the decoded output — the show, mosh included.
- Dry is the encoder input — the source before the mosh.
- Clean displays the stored frame directly, with no generation loss.
- Mosh feeds the stored frame back through the encoder, so the current program chews the memory.
u switches between wet and dry. They are captured on the same tick, so the
switch is a frame-exact A/B at the same playhead. n switches between clean and
mosh playback. In Mosh mode the dry track is often the more interesting one: it
re-performs an old moment under the current settings rather than replaying the
old result.
The loop window
The loop window is a slice inside the captured bank. The loop length sets its size, and the position moves its centre: 0 is the earliest window, 1 is the latest. The window is per bank, so switching away and back restores where you left it. A fresh capture starts on the most recent window; shortening keeps the window's end fixed, so a one-frame loop freezes the newest frame.
Recording
Clean mode keeps recording the live pipeline, so you can capture a new moment while watching an older loop. Mosh mode pauses recording, because the encoder input is the tape itself and recording would capture the feedback path.
Banks share the frame pool rather than copying it, so eight overlapping loops are cheap. If the pool fills, the oldest captured bank is evicted.
Presets capture the tape's transport — loop length, loop mode, track and Clean/Mosh — but never the frames. Bank contents are performance material, not configuration.
Order modes
The order layer reorders packets inside the window. It adds about eight frames of video latency; control latency is unaffected.
| Mode | What it does |
|---|---|
| none | No reordering |
| Reverse | Reverses the packet order in the window |
| SortByMotion | Reorders packets by motion |
| Shuffle | Shuffles the packet order |
| Hold | Holds one moment's motion; it can read as a freeze |
Use o or MUTE 6 to cycle the modes. MUTE 1–5 select a mode directly.
Presets
A preset captures the whole program: the ops in order, their parameters, the seed, the tempo, the encoder settings and the tape transport. It never captures the tape's frames. Recalling a preset and feeding the same input reproduces the same output.
- REW and FF recall presets, and the MARKER SET button saves.
Presets live in the app's data folder.
Encoder
Codec, bitrate and GOP are live controls.
CC36cycles the codec: mpeg4 → mpeg2video → h263p → libx264 → libvpx.CC17halves and doubles the bitrate. A lower bitrate is coarser and blooms more; a higher bitrate is finer.CC21steps the GOP up and down by five frames.
A codec change rebuilds the decoder and forces a keyframe, so the stream recovers immediately. An invalid setting rolls back to the previous working encoder.
The default codec is mpeg4. Change codecs in discrete steps; sweeping through them is not useful.
MIDI — nanoKONTROL2 default map
MoshBox opens a MIDI port by name. The default match is nanokontrol; set
MOSHBOX_MIDI to use a differently named or virtual port.
The default map splits by function: the channel strips play, the SOLO row is the mosh and tape layer, the MUTE row is the order layer, the REC row is the eight tape banks, and the master/transport section navigates presets and sources.
Faders
| Fader | CC | Function |
|---|---|---|
| 1 | 0 | Repeat count — how many times each delta is replayed |
| 2 | 1 | Stutter rate — probability a delta is stuttered |
| 3 | 2 | Bloom burst — how many recent deltas are replayed as a burst |
| 4 | 3 | Corrupt rate — probability a payload is damaged |
| 5 | 4 | Corrupt amount — fraction of the payload zeroed |
| 6 | 5 | Pulse division — subdivisions per beat |
| 7 | 6 | Pulse hold — how many subdivisions stay open |
| 8 | 7 | Stutter count — copies emitted when a delta stutters |
Pots
| Pot | CC | Function |
|---|---|---|
| 1 | 16 | Source switch — 0 smear, 1 clean |
| 2 | 17 | Bitrate |
| 3 | 18 | Tape length — 1 to 120 frames |
| 4 | 19 | Motion amount — depth of motion-driven repeat modulation |
| 5 | 20 | Motion max — ceiling the modulation can reach |
| 6 | 21 | GOP — keyframe interval |
| 7 | 22 | Tape window position — 0 earliest, 1 latest |
Pot 8 (CC 23) is unbound.
Buttons
| Button | CC | Function |
|---|---|---|
| SOLO 1 | 32 | Toggle the mosh |
| SOLO 2 | 33 | Normalize |
| SOLO 3 | 34 | Toggle the rhythm layer |
| SOLO 4 | 35 | Toggle motion modulation |
| SOLO 5 | 36 | Cycle the codec |
| SOLO 6 | 37 | Reset (normalize) |
| SOLO 7 | 38 | Toggle wet / dry |
| SOLO 8 | 39 | Toggle clean / mosh |
| MUTE 1 | 48 | Order: none |
| MUTE 2 | 49 | Order: Reverse |
| MUTE 3 | 50 | Order: SortByMotion |
| MUTE 4 | 51 | Order: Shuffle |
| MUTE 5 | 52 | Order: Hold |
| MUTE 6 | 53 | Cycle order |
| REC 1–8 | 64–71 | Tape banks 1–8 |
| PLAY | 41 | Toggle loop / live |
| STOP | 42 | Freeze or resume the playhead |
| REW | 43 | Previous preset |
| FF | 44 | Next preset |
| REC (transport) | 45 | Capture into the active bank |
| CUE | 46 | Cycle the tape loop mode |
| MARKER SET | 60 | Save preset |
| MARKER LEFT | 61 | Clear the active bank |
| MARKER RIGHT | 62 | Restart the active loop |
| TRACK left | 58 | Previous source |
| TRACK right | 59 | Next source |
MUTE 7 and MUTE 8 (CC 54/55) are unbound. The default map leaves three of the 51 controls empty on purpose.
The buttons are momentary CCs; each press fires once.
Not every fader has something to drive in the default program. The default program is SourceSwitch + DropKey + RepeatDelta, so the repeat count, source switch, encoder, mosh, order, tape and preset controls are live immediately. The Stutter, Bloom and Corrupt faders become active when a preset carries those ops; the Pulse faders need the rhythm layer, and the motion pots need motion modulation.
Bindings live in assets/control-map.json; edit the map and relaunch, with no
rebuild. The map is validated strictly, so a bad binding is reported rather than
silently ignored.
Troubleshooting
| Symptom | What to try |
|---|---|
| No picture | Check the HDMI connection. If no camera is attached, make sure there is a video file in the data folder for the clip fallback. |
| Camera not detected | Reseat the capture device; C cycles capture devices; check that it is UVC-compatible. |
| The mosh is not engaging | Press mosh (space or SOLO 1). Check the HUD: Idle is a clean pass-through. |
| The image goes clean after a moment | Normalize or the Recovering state has re-anchored the picture. Press mosh again. |
| Rhythm does not gate | Turn on the rhythm layer (p or SOLO 3) and check the clock and BPM. |
| Motion does nothing | Turn on motion (m or SOLO 4) and raise the MOTION AMOUNT and MOTION MAX pots. |
| An order mode freezes the picture | Hold is a freeze by design; choose another order mode. |
| A source switch is a hard cut | The default is clean. Turn the SOURCE SWITCH pot toward smear. |
| A source switch smears when you want a cut | Turn the SOURCE SWITCH pot toward clean. |
| A codec change glitches | Expected. The decoder rebuilds and forces a keyframe. |
| MIDI is not responding | Check the controller is connected; MoshBox matches the port name nanokontrol. Set MOSHBOX_MIDI for another device. |
| A control does nothing | The default program is SourceSwitch + DropKey + RepeatDelta. Stutter, Bloom and Corrupt need a preset that includes the op; the Pulse faders need the rhythm layer. |
| A tape bank does nothing | An empty bank captures on the first press; x captures into the active bank. Check the HUD for tape off if the tape was disabled. |
| The tape plays but the mosh does not | Clean playback is selected. Press n or SOLO 8 to switch to Mosh. |
| The tape window will not move | The loop length is the same as the bank size. Shorten the loop, then move the window. |
| Mosh mode stops recording | Expected. Mosh playback uses the encoder for the tape, so recording pauses until you return to Clean or live. |
| The tape uses too much memory | Reduce MOSHBOX_TAPE_FRAMES, set MOSHBOX_TAPE_DRY=0, or disable the tape with MOSHBOX_TAPE=0. |
Specifications
| Input | 1× USB UVC camera or capture device |
| Output | HDMI: 640×480 4:3 SD on Pi 3; 1280×720 16:9 HD Ready on the 720 build |
| Processing | Encoder → mosh grammar → decoder |
| Frame tape | 8 banks, 1–120 frames (4 s at 30 fps), wet/dry, clean/mosh, forward/ping-pong/reverse |
| Control | MIDI (nanoKONTROL2 default); keyboard on desktop builds |
| Presets | Ops, parameters, seed, tempo, encoder settings, tape transport |
| Platform | Raspberry Pi 3 / Pi 4 |
| Power | 5 V / 2.5 A / 5.1 V 3.0 A recommended |
Credits
MoshBox's behaviour is informed by Interstream v2.0 by Jonas Bers / Signal Culture.