npm.io
2.0.0 • Published 1 month ago

launchpad-webmidi

Licence
MIT
Version
2.0.0
Deps
0
Size
124 kB
Vulns
0
Weekly
0
Stars
30

Launchpad WebMIDI

npm version license

Drive a Novation Launchpad Mini straight from the browser, using the Web MIDI API – no server, no native module, no build step required.

import Launchpad from 'launchpad-webmidi';

const pad = new Launchpad();
await pad.connect();

pad.reset(2);                       // every LED to medium-brightness yellow
pad.on('key', k => {
    pad.col(k.pressed ? pad.red : pad.green, k);
});

Full API reference: docs/API.md

Contents

Requirements

A Novation Launchpad Mini (or a device whose MIDI port name contains Launchpad), and a browser that implements the Web MIDI API.

Browser support is the real constraint here, so it's worth being precise:

Browser Support
Chrome 43+
Edge 79+
Opera 30+
Chrome for Android 152+
Firefox 108+, but see the caveat below
Safari (macOS and iOS) Not supported

Two things will bite you:

The page must be a secure context. navigator.requestMIDIAccess() is only exposed over HTTPS or on localhost. Opening an example straight from the filesystem with file:// will not work – serve it instead:

npx serve .    # then open http://localhost:3000/examples/basic-with-esm.html

Chrome 124 and later always prompt for permission. Permission used to be required only for SysEx messages; it now covers all MIDI access. Your users will see a prompt on the first connect(), and connect() rejects if they decline.

Firefox needs a site-permission add-on. Although Firefox has shipped Web MIDI since 108, requestMIDIAccess() rejects on every origin except localhost unless the user has installed a site-specific .xpi add-on that you host yourself. Firefox cannot distinguish "add-on missing" from "user said no", so neither can this library. In practice, treat Firefox as unsupported unless you are prepared to ship an add-on.

If your page is embedded in an iframe, the parent must also grant MIDI through the Permissions-Policy: midi header.

Installation

npm install launchpad-webmidi
yarn add launchpad-webmidi

Or drop a build straight into a page from a CDN:

<script type="module">
    import Launchpad from 'https://unpkg.com/launchpad-webmidi/dist/launchpad-webmidi.es.js';
</script>

Choosing a build

Rollup produces three bundles from the same source. They differ only in how they expose the Launchpad class.

File Format How you get Launchpad Use when
dist/launchpad-webmidi.es.js ES module import Launchpad from … (default export) Almost always. Any modern bundler, or a plain <script type="module">
dist/launchpad-webmidi.umd.js UMD global Launchpad, or AMD/CommonJS You need AMD, or a global from a plain <script>
dist/launchpad-webmidi.js IIFE global Launchpad A plain <script> tag and nothing else

Prefer the ES module build unless you have a specific reason not to.

Getting started

With ES modules
<script type="module">
    import Launchpad from '../dist/launchpad-webmidi.es.js';

    const pad = new Launchpad();

    pad.connect().then(() => {
        pad.reset(2);

        pad.on('key', k => {
            console.log(`Key ${k.x},${k.y} down: ${k.pressed}`);
            // Amber while held, green once released
            pad.col(k.pressed ? pad.amber : pad.green, k);
        });
    }).catch(err => {
        console.error('Could not reach the Launchpad:', err);
    });
</script>
With a global build
<script src="../dist/launchpad-webmidi.umd.js"></script>
<script>
    const pad = new Launchpad();
    pad.connect().then(() => pad.reset(2));
</script>
Lighting specific buttons

The key event object doubles as an [x, y] pair, so it can be handed straight back to col(). You can also address buttons explicitly:

pad.col(pad.red, [0, 0]);                      // one button
pad.col(pad.green, [[0, 0], [1, 0], [2, 0]]);  // several at once

// A whole row, described as a string
pad.col(pad.amber, pad.fromMap(
    'xxxxxxxx.' +
    '.........' +
    '.........' +
    '.........' +
    '.........' +
    '.........' +
    '.........' +
    '.........' +
    '.........'
));

Button layout and coordinates

Every button is addressed as an [x, y] pair. x runs left to right, y runs top to bottom, both starting at 0.

            Automap / Live buttons          y = 8
        (0,8)(1,8)(2,8)(3,8)(4,8)(5,8)(6,8)(7,8)
         ○    ○    ○    ○    ○    ○    ○    ○

        ┌────┬────┬────┬────┬────┬────┬────┬────┐
 y = 0  │ 0,0│ 1,0│ 2,0│ 3,0│ 4,0│ 5,0│ 6,0│ 7,0│  ○ (8,0)  ┐
        ├────┼────┼────┼────┼────┼────┼────┼────┤           │
 y = 1  │ 0,1│ 1,1│ 2,1│ 3,1│ 4,1│ 5,1│ 6,1│ 7,1│  ○ (8,1)  │
        ├────┼────┼────┼────┼────┼────┼────┼────┤           │  Scene
  ...   │  · │  · │  · │  · │  · │  · │  · │  · │  ○  ...   │  buttons
        ├────┼────┼────┼────┼────┼────┼────┼────┤           │  x = 8
 y = 7  │ 0,7│ 1,7│ 2,7│ 3,7│ 4,7│ 5,7│ 6,7│ 7,7│  ○ (8,7)  ┘
        └────┴────┴────┴────┴────┴────┴────┴────┘
                    8 × 8 grid

Three groups, matching the three kinds of button on the hardware:

  • Grid – the 64 square buttons, x and y both 0–7.
  • Scene – the 8 round buttons down the right-hand side, x = 8, y 0–7.
  • Automap / Live – the 8 round buttons along the top, y = 8, x 0–7.

Two quirks are worth internalising, because they trip everyone up:

y = 8 is physically the top row. The Automap buttons sit above row y = 0 on the device, even though they carry the highest y. The coordinate system was inherited from launchpad-mini and kept for compatibility.

There are 80 buttons, not 81. The corner position (8, 8) – top-right, where the Automap row and the Scene column would meet – has no button on the hardware, so it does not exist in the coordinate space either. Passing [8, 8] to col() is a no-op that resolves to false.

Under the hood the two families speak different MIDI dialects: grid and scene buttons use Note On (0x90), Automap buttons use Control Change (0xb0). The library hides this, but it explains why y = 8 is special-cased everywhere.

Colours

The Launchpad Mini has red and green LEDs per button. Mixing them gives amber, and a fixed red/green ratio gives yellow – that is the entire palette.

Five colours are available as properties on the pad:

pad.red
pad.green
pad.amber
pad.yellow
pad.off

Each is a Color object, and each has three brightness levels:

pad.col(pad.red.low, [0, 0]);
pad.col(pad.red.medium, [1, 0]);
pad.col(pad.red.full, [2, 0]);   // same as pad.red

Colours are immutable – .low returns a new Color rather than modifying the original, so pad.red is always full-brightness red.

Switch an LED off with pad.off, or by passing the raw code 0:

pad.col(pad.off, [0, 0]);
pad.col(0, [0, 0]);      // equivalent

See the Colour model in the API reference for double-buffering modifiers, the MIDI encoding, and why yellow only has one brightness.

Examples

Runnable pages live in examples/. Serve the repository over HTTP and open them – remember that file:// will not work.

Example What it shows
basic-with-esm.html Minimal connect-and-react loop, ES modules
basic-with-umd.html The same, via the UMD global
switch-color-with-esm.html Per-button colour state that cycles on each press
switch-color-with-umd.html The same, via the UMD global

Coming from launchpad-mini

This library began as a port of launchpad-mini, Simon Eugster's Node library for the same hardware. Most of the API is unchanged, so existing code often ports with only the connection code rewritten.

What changed:

launchpad-mini launchpad-webmidi
Runtime Node.js Browser
MIDI backend the native midi module Web MIDI API
Modules CommonJS (require) ES modules (import)
Import const Launchpad = require('launchpad-mini') import Launchpad from 'launchpad-webmidi'
Connecting pad.connect() → promise, optional port name pad.connect() → promise, auto-detect only
Permissions none secure context plus a browser permission prompt

What stayed the same: the [x, y] coordinate system, the Color model and its brightness levels, col(), setColors(), reset(), isPressed(), pressedButtons, fromMap(), brightness(), multiplexing(), and the key event and its array-like payload.

What was dropped: port selection by name or index, and everything in launchpad-mini that depended on Node APIs.

Known limitations

None outstanding. Every defect documented in 1.2.x was fixed in 1.3.0 and 2.0.0 – see the changelog.

Two hardware constraints are worth knowing, since they look like bugs:

  • Yellow has only one brightness. Its MIDI code hard-codes the red/green pair that reads as yellow, leaving no room to encode a level.
  • There is no button at (8, 8). The top-right corner, where the Automap row would meet the Scene column, does not exist on the device. It is ignored wherever it can be addressed.

Changelog

See CHANGELOG.md.

Licence

MIT

Keywords