Installation

Scribbletune can be used as a Node.js module to export MIDI files from the terminal OR in the browser along with Tone.js.

Node Module

Install Scribbletune from npm

npm i scribbletune

Now you can create MIDI files from the terminal

const scribble = require('scribbletune');

const clip = scribble.clip({
  notes: scribble.scale('C4 major'),
  pattern: 'x'.repeat(8)
});

scribble.midi(clip); // creates a file called music.mid in the same location as this script was created and run.

Browser

There are a couple of ways to use Scribbletune in the browser.

Use the latest available precompiled version of Scribbletune from CDNjs and reference it in your HTML right after Tone.js (replace LATEST_VERSION_FROM_CDNJS with the latest version from CDNjs).

<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.7.43/Tone.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/scribbletune/LATEST_VERSION_FROM_CDNJS/scribbletune.js"></script>

This will make a global object called scribble available for you to use in your script.

console.log(scribble.scale('C4 major')); // outputs ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4']

Install scribbletune and webpack from npm

npm i scribbletune
npm i vite --save-dev

Create a bare minimum vite.config.js file with

import { defineConfig } from "vite";

export default defineConfig({});

Create a file called script.js and enter the following in there

import { Session } from "scribbletune/browser";

const btnStart = document.getElementById("start");
const btnPlay1 = document.getElementById("play1");
const btnPlay2 = document.getElementById("play2");
const btnPlay3 = document.getElementById("play3");
const btnStop = document.getElementById("stop");
let channel;
let isReady = false;

btnStart.addEventListener("click", async () => {
  // Resume audio context (required for browsers)
  await Tone.start();
  console.log("Audio context started");

  // Create session and channel
  const session = new Session();
  channel = session.createChannel({
    instrument: "PolySynth",
    clips: [
      { pattern: "x-x-", notes: "C4 E4 G4" },
      { pattern: "x[xx]", notes: "C#4 D#4" },
      { pattern: "[xxxx]", notes: ["G4", "D5"] },
    ],
    eventCb: (event, data) => {
      console.log("Channel event:", event, data);
      if (event === "loaded") {
        isReady = true;
        console.log("Channel is ready to play!");
      }
      if (event === "error") {
        console.error("Channel error:", data.e);
      }
    },
  });

  // Start the transport
  Tone.Transport.start();
  console.log("Transport started");
});

btnPlay1.addEventListener("click", () => {
  if (!isReady) {
    console.log("Channel not ready yet. Click 'Start context' first.");
    return;
  }
  console.log("Starting clip 0");
  channel.startClip(0);
});

btnPlay2.addEventListener("click", () => {
  if (!isReady) {
    console.log("Channel not ready yet. Click 'Start context' first.");
    return;
  }
  console.log("Starting clip 1");
  channel.startClip(1);
});

btnPlay3.addEventListener("click", () => {
  if (!isReady) {
    console.log("Channel not ready yet. Click 'Start context' first.");
    return;
  }
  console.log("Starting clip 2");
  channel.startClip(2);
});

btnStop.addEventListener("click", () => {
  if (channel) {
    console.log(`Stopping clip ${channel.activeClipIdx}`);
    channel.stopClip(channel.activeClipIdx);
  }
});

Now create a file called index.html and enter the following in it

<!doctype html>
<html>
  <head>
    <title>Testing Scribbletune</title>
  </head>
  <body>
    <div>
      <p>The Audio context needs to be started by a user gesture.</p>
      <button id="start">Start context</button>
    </div>
    <div>
      <p>
        Once audio context is started by a user gesture, you can use the
        start/stop methods on clip objects. The clips will align per bar, so if
        you start a clip in the middle of a bar, it will wait until the next bar
        to start playing.
      </p>
      <button id="play1">Play Clip 1</button>
      <button id="play2">Play Clip 2</button>
      <button id="play3">Play Clip 3</button>
      <hr />
      <button id="stop">Stop</button>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.7.43/Tone.js"></script>
    <script type="module" src="/script.js"></script>
  </body>
</html>

Run the following command to start a dev server locally,

npx vite

Now load http://localhost:5173/ in your browser and click the start button to start the audio context. After that, click Play to hear the C4, E4, G4 notes played in a loop at an interval of a quarter note.