npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

launchpad-webmidi

v2.0.0

Published

Interacting with a Novation Launchpad from your browser with the web MIDI API

Readme

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