Skip to main content

PipSqueak

A joystick and a button from useMidi. Thanks to Masson there for providing them. Two axes, each a control change 0 to 127, and a button that is also a control change. Some on the table were set up with the configurator, so the helper takes a config instead of assuming the numbers below.

What it sends​

ControlMessageDefaultNotes
Left and rightCCCC 170 left, 126 right. Rest is around 60, not 64: the centre is off-centre on purpose and the helper calibrates for it.
Up and downCCCC 200 down, 126 up. Rest around 68.
ButtonCCCC 25Above 64 while held, 0 when released.
USB namecontains "PipSqueak"Some units enumerate as "PipSqueaker". The Explorer prints the exact name.

Defaults measured from a real unit on September 12, 2026. A custom unit may use other numbers. Check the Explorer and pass a config.

Start here​

It's under 40 lines. Open it, press Run, and push the stick. The change this comments mark the lines to make your own, and Hello world walks through this exact sketch line by line.

Open starters/java/PipSqueakStarter/PipSqueakStarter.pde.

PipSqueakStarter.pde
// PipSqueak starter: the joystick pushes a dot around, the button changes its colour.
// No stick: arrows move, space is the button. Edit the lines marked "change this".
PipSqueak stick;
float px, py, hue = 200;

void setup() {
size(800, 600);
colorMode(HSB, 360, 100, 100, 100);
background(0, 0, 8);
px = width / 2;
py = height / 2;
stick = new PipSqueak(this); // change this: new PipSqueak(this, "name") if yours has another name
stick.connect(); // prints the MIDI devices it found; fine with no device
}

void draw() {
px = constrain(px + stick.x * 6, 0, width); // stick.x is -1..1. change this: 6 is the speed
py = constrain(py - stick.y * 6, 0, height); // stick.y is +1 when pushed up; screen y grows downward
if (stick.justPressed()) hue = (hue + 47) % 360; // change this: what a tap of the button does
fill(0, 0, 8, 12); // change this: a lower alpha leaves a longer trail
rect(0, 0, width, height);
fill(hue, 80, 100);
float size = 40 + stick.magnitude * 30 + (stick.pressed ? 40 : 0);
circle(px, py, size); // change this: what gets drawn
}

The helper​

new PipSqueak(this), new PipSqueak(this, "name", config)Connects by USB-name substring. The config carries CC numbers, centre and range per axis, the button CC and threshold, deadzone and smoothing.
stick.x, stick.y-1 to 1. y is +1 when pushed up, so subtract it from a screen y.
stick.angle, stick.magnitudeRadians and 0 to 1. The angle is NaN inside the deadzone, so test magnitude first.
stick.pressed, stick.justPressed(), stick.justReleased()The button. The "just" ones are true for one frame.
void stickPressed(), void stickReleased()Define either in your sketch and the helper calls it.

From the helpers' README​

A joystick and one button. It sends Control Change. Units are configured at usemidi.com/configure.html, so the map is a config, not a constant.

PipSqueak stick = new PipSqueak(this); // or (this, "name"), (this, "name", config)
stick.connect(); stick.connected();
stick.x; stick.y; // -1..1, y is +1 pushed up
stick.angle; stick.magnitude; // radians (0 = right, counter-clockwise positive) / 0..1. angle is NaN in the deadzone
stick.pressed; stick.justPressed(); stick.justReleased();
stick.rawX; stick.rawY; // last CC values, 0..127
stick.recenter(); // hands off for 0.5 s; the resting position becomes the centre. recenter(seconds) too
stick.flash(); // Note On 60 velocity 127 then Note Off, to the stick's own port: the LED flashes red for 200 ms
stick.send(status, d1, d2); // any message to the stick, for rules you saved in the useMidi configurator

Callbacks: stickPressed(), stickReleased(). Stand-in: arrow keys and SPACE.

The LED is rule-based. The stick takes no colour commands. It stores rules set in the useMidi configurator ("when this message arrives, do this to the LED") and the helper only sends. George's units run firmware 2.7.0-beta.3 with one rule: Note On 60 on any channel flashes the LED red for 200 ms. flash() sends that pair. send() is for other rules. The output port opens on the first send; connect() only opens the input. No output port (older firmware, no stick): both calls do nothing after one console line. Python: stick.flash(), stick.send(status, d1, d2). p5: the same, with the port opened asynchronously and the first messages queued in order.

Config is a PipSqueakConfig (public fields xCC, xCenter, xMin, xMax, xInvert, yCC..., buttonCC, buttonThreshold, buttonNote, deadzone, smoothing, name) or a JSONObject in the shape the JS and Python helpers use. One pipsqueak.json serves all three:

{ "name": "pipsqueak",
"x": {"cc": 17, "center": 60, "min": 0, "max": 126, "invert": false},
"y": {"cc": 20, "center": 68, "min": 0, "max": 126, "invert": false},
"button": {"cc": 25, "threshold": 64},
"deadzone": 0.1, "smoothing": 1.0 }

new PipSqueak(this, "pipsqueak", loadJSONObject("pipsqueak.json")). A button that sends a note is "button": {"note": 60}. smoothing is a moving average factor: 1 is none, 0.3 is heavy. The rest position is off-centre on purpose; that is what the unit does.

Message maps. Two have been seen on real units. With no config the Java helper knows both: it starts on stock and switches once to another known map the moment a message arrives that only that map claims. It also snaps each axis centre to the first value it sees when that value is within 12 of the map's rest, because the first message is sent as the stick leaves rest. stick.config.profile says which map is live. The console prints the switch.

Profilexybuttonmeasured
stock (default)CC 17, rest 60CC 20, rest 68CC 25, value ≥ 642026-09-12
usemidiCC 10, rest 67CC 7, rest 70note 60 on / off (velocity 127 both ways)2026-10-02, a unit named PipSqueaker

Any channel. A JSON or PipSqueakConfig you pass means what it says (auto off unless the JSON says "auto": true). A third map: read it off the Explorer, then add a profile to PipSqueakConfig.profiles() or ship a JSON. The Python and JS helpers only carry the stock defaults; give them a config for a usemidi unit. Normalisation is piecewise-linear around the centre, then a radial deadzone, then a rescale so magnitude runs 0..1 from the deadzone edge to full deflection. Same maths as pipsqueak.js.

Python: PipSqueak(this, name=None, config=None) with a dict config; stick.x, stick.angle (None in the deadzone), stick.just_pressed(), stick.events() ("press"/"release" since last call), recenter(). The old PipSqueak(config) form from pipsqueak/pipsqueak_py still works; then you call update().

Try first​

  • A dot with a trail. Then colour the trail by angle.
  • A character whose speed is the magnitude. Then walls.
  • Hold the button to draw, release to lift the pen.
  • Steer a parameter, not a position. The Gray-Scott demo does this.

More on the ideas page and the demos page.

Without the helper​

Three CCs decoded inline: 17 is x, 20 is y, 25 is the button. Under sixty lines, same stand-ins. Read the three bytes, no helpers first.