jsPlayCommand: xTalk Scripted Music Notation in JavaScript

The jsPlayCommand library is a JavaScript implementation of the play command from HyperTalk, which used a kind of notation now known in the xTalk community as scripted music notation. HyperTalk used sound resources, but with our Modern Technology™ * we can use MIDI!

You can play around with jsPlayCommand using the editor below. Load a preset song or enter your own using scripted music notation. The equivalent traditional music notation will appear above the text field as you type. Scroll down the page for a reference to scripted music notation. Then, hit the Play button to see how it sounds! You will have to either use Google Chrome or install Jazz-Soft.net Jazz-Plugin to be able to actually play the notes. Alternatively, you can click the Generate MIDI File button and download your creation as a standard MIDI file you can play anywhere!

Load preset:

Using Scripted Music Notation

Scripted music notation uses a sequence of note strings separated by whitespace to represent a sequence of notes. Each note string representing a sounded note is composed of four parts: a pitch, a duration, a velocity, and an effect. A note string representing a rest is composed of two parts: a duration and the letter r.

Using jsPlayCommand

To use jsPlayCommand, embed the following script tags on your page:

<script type="text/javascript" src="http://www.kreativekorp.com/lib/jsPlayCommand/WebMIDIAPI.js"></script>
<script type="text/javascript" src="http://www.kreativekorp.com/lib/jsPlayCommand/jsPlayCommand.js"></script>

Or, if you prefer to host it yourself, download jsPlayCommand.zip.

You may need to install Jazz-Soft.net Jazz-Plugin to be able to use jsPlayCommand (unless, of course, your browser already supports the Web MIDI API, currently found only in Chrome Canary).

Before jsPlayCommand can actually play any notes, it must be initialized using the following APIs:

jsPlayCommand.start(success, failure)
Requests access to MIDI controllers and initializes jsPlayCommand. The success function is called if this completed successfully. Otherwise, the failure function is called.
jsPlayCommand.running()
Returns true if jsPlayCommand.start() has been called successfully.
jsPlayCommand.stop()
Resets jsPlayCommand to its uninitialized state.

Once jsPlayCommand is initialized, use one of the following APIs:

jsPlayCommand.play(instrument, notes)
jsPlayCommand.play(instrument, tempo, notes)
jsPlayCommand.play(channel, instrument, tempo, notes)
jsPlayCommand.play(bankSelect, channel, instrument, tempo, notes)
jsPlayCommand.play(output, bankSelect, channel, instrument, tempo, notes)
Plays a sequence of notes on a MIDI device. The sequence is played asynchronously, so this API returns almost immediately. The parameters to this API are:
  • Output is one of the devices returned by jsPlayCommand.outputs().
  • BankSelect is the bank select mode, which is one of the following:
    jsPlayCommand.BANK_SELECT_GM General MIDI mode. No bank select commands are sent.
    jsPlayCommand.BANK_SELECT_GS GS mode. CC0 is used for bank select; CC32 is not used.
    jsPlayCommand.BANK_SELECT_XG XG mode. CC32 is used for bank select; CC0 is not used.
    jsPlayCommand.BANK_SELECT_MMA MMA or GM2 mode. CC0 is used for MSB; CC32 is used for LSB.
  • Channel is the MIDI channel number from 0 to 15.
  • Instrument is an integer, an object with a bank and instrument, or one of the following instrument names:
    0Acoustic Grand Piano
    1Bright Acoustic Piano
    2Electric Grand Piano
    3Honky-Tonk Piano
    4Electric Piano 1
    5Electric Piano 2
    6Harpsichord
    7Clavi
    8Celesta
    9Glockenspiel
    10Music Box
    11Vibraphone
    12Marimba
    13Xylophone
    14Tubular Bells
    15Dulcimer
    16Drawbar Organ
    17Percussive Organ
    18Rock Organ
    19Church Organ
    20Reed Organ
    21Accordion
    22Harmonica
    23Tango Accordion
    24Acoustic Guitar (Nylon)
    25Acoustic Guitar (Steel)
    26Electric Guitar (Jazz)
    27Electric Guitar (Clean)
    28Electric Guitar (Muted)
    29Overdriven Guitar
    30Distortion Guitar
    31Guitar Harmonics
    32Acoustic Bass
    33Electric Bass (Finger)
    34Electric Bass (Pick)
    35Fretless Bass
    36Slap Bass 1
    37Slap Bass 2
    38Synth Bass 1
    39Synth Bass 2
    40Violin
    41Viola
    42Cello
    43Contrabass
    44Tremolo Strings
    45Pizzicato Strings
    46Orchestral Harp
    47Timpani
    48String Ensemble 1
    49String Ensemble 2
    50Synth Strings 1
    51Synth Strings 2
    52Choir Aahs
    53Voice Oohs
    54Synth Voice
    55Orchestra Hit
    56Trumpet
    57Trombone
    58Tuba
    59Muted Trumpet
    60French Horn
    61Brass Section
    62Synth Brass 1
    63Synth Brass 2
    64Soprano Sax
    65Alto Sax
    66Tenor Sax
    67Baritone Sax
    68Oboe
    69English Horn
    70Bassoon
    71Clarinet
    72Piccolo
    73Flute
    74Recorder
    75Pan Flute
    76Blown Bottle
    77Shakuhachi
    78Whistle
    79Ocarina
    80Lead 1 (Square)
    81Lead 2 (Sawtooth)
    82Lead 3 (Calliope)
    83Lead 4 (Chiff)
    84Lead 5 (Charang)
    85Lead 6 (Voice)
    86Lead 7 (Fifths)
    87Lead 8 (Bass + Lead)
    88Pad 1 (New Age)
    89Pad 2 (Warm)
    90Pad 3 (Polysynth)
    91Pad 4 (Choir)
    92Pad 5 (Bowed)
    93Pad 6 (Metallic)
    94Pad 7 (Halo)
    95Pad 8 (Sweep)
    96FX 1 (Rain)
    97FX 2 (Soundtrack)
    98FX 3 (Crystal)
    99FX 4 (Atmosphere)
    100FX 5 (Brightness)
    101FX 6 (Goblins)
    102FX 7 (Echoes)
    103FX 8 (Sci-Fi)
    104Sitar
    105Banjo
    106Shamisen
    107Koto
    108Kalimba
    109Bagpipe
    110Fiddle
    111Shanai
    112Tinkle Bell
    113Agogo
    114Steel Drums
    115Woodblock
    116Taiko Drum
    117Melodic Tom
    118Synth Drum
    119Reverse Cymbal
    120Guitar Fret Noise
    121Breath Noise
    122Seashore
    123Bird Tweet
    124Telephone Ring
    125Helicopter
    126Applause
    127Gunshot
    When matching an instrument name, case, whitespace, and punctuation are ignored, so "Alto Sax", " alto_sax ", and "ALTO.SAX" all map to instrument 65.
  • Tempo is the tempo of the note sequence in beats per minute.
  • Notes is a sequence of notes in scripted music notation.
jsPlayCommand.playMultiple(arguments)
jsPlayCommand.playMultiple(arguments, arguments)
jsPlayCommand.playMultiple(arguments, arguments, arguments, ...)
Executes several play commands in parallel. Even though the play command is asynchronous, there is still a delay introduced by processing arguments, parsing note strings, and so on; this ensures that all the sequences passed in start playing at exactly the same time. Each argument to this API is an array of arguments as given to jsPlayCommand.play(). If you need to pass in an array of arrays, as this demo does, use jsPlayCommand.playMultiple.apply(jsPlayCommand, arrays).
jsPlayCommand.compile(instrument, notes)
jsPlayCommand.compile(instrument, tempo, notes)
jsPlayCommand.compile(channel, instrument, tempo, notes)
jsPlayCommand.compile(bankSelect, channel, instrument, tempo, notes)
jsPlayCommand.compile(trackName, bankSelect, channel, instrument, tempo, notes)
Compiles a sequence of notes into a MIDI file and returns the MIDI file as an array of byte values. The parameters to this API are similar to the parameters to jsPlayCommand.play(), except that a track name is given in place of an output device. This API will always work, independently of jsPlayCommand initialization or MIDI support.
jsPlayCommand.compileMultiple(arguments)
jsPlayCommand.compileMultiple(arguments, arguments)
jsPlayCommand.compileMultiple(arguments, arguments, arguments, ...)
Compiles multiple sequences of notes into a MIDI file and returns the MIDI file as an array of byte values. The parameters to this API are similar to the parameters to jsPlayCommand.playMultiple(), except that track names are given in place of output devices. This API will always work, independently of jsPlayCommand initialization or MIDI support.
jsPlayCommand.compileBase64(instrument, notes)
jsPlayCommand.compileBase64(instrument, tempo, notes)
jsPlayCommand.compileBase64(channel, instrument, tempo, notes)
jsPlayCommand.compileBase64(bankSelect, channel, instrument, tempo, notes)
jsPlayCommand.compileBase64(trackName, bankSelect, channel, instrument, tempo, notes)
Compiles a sequence of notes into a MIDI file and returns the MIDI file as a base64-encoded string. The parameters to this API are similar to the parameters to jsPlayCommand.play(), except that a track name is given in place of an output device. This API will always work, independently of jsPlayCommand initialization or MIDI support.
jsPlayCommand.compileMultipleBase64(arguments)
jsPlayCommand.compileMultipleBase64(arguments, arguments)
jsPlayCommand.compileMultipleBase64(arguments, arguments, arguments, ...)
Compiles multiple sequences of notes into a MIDI file and returns the MIDI file as a base64-encoded string. The parameters to this API are similar to the parameters to jsPlayCommand.playMultiple(), except that track names are given in place of output devices. This API will always work, independently of jsPlayCommand initialization or MIDI support.

By default, jsPlayCommand uses the first output device it finds, does not use any bank select commands, and plays on channel 0 with instrument 0 at a tempo of 120 bpm, unless a play command specifies otherwise. These defaults can be changed with the following APIs:

jsPlayCommand.outputs()
Returns a list of MIDI output devices where jsPlayCommand can send its output.
jsPlayCommand.getDefaultOutput()
jsPlayCommand.setDefaultOutput(output)
Gets or sets the default MIDI output device.
jsPlayCommand.getDefaultBankSelect()
jsPlayCommand.setDefaultBankSelect(bankSelect)
Gets or sets the default bank select mode. This must be one of the following:
jsPlayCommand.BANK_SELECT_GM General MIDI mode. No bank select commands are sent.
jsPlayCommand.BANK_SELECT_GS GS mode. CC0 is used for bank select; CC32 is not used.
jsPlayCommand.BANK_SELECT_XG XG mode. CC32 is used for bank select; CC0 is not used.
jsPlayCommand.BANK_SELECT_MMA MMA or GM2 mode. CC0 is used for MSB; CC32 is used for LSB.
jsPlayCommand.getDefaultChannel()
jsPlayCommand.setDefaultChannel(channel)
Gets or sets the default MIDI channel.
jsPlayCommand.getDefaultInstrument()
jsPlayCommand.setDefaultInstrument(instrument)
Gets or sets the default instrument.
jsPlayCommand.getDefaultTempo()
jsPlayCommand.setDefaultTempo(tempo)
Gets or sets the default tempo.

If you would like to play around with the API, there is a jsPlayCommand JSFiddle for you!

Additional Resources

HyperSound is a soundfont recreation of the original three sampled sounds included with HyperCard for use with the play command. The "harpsichord" sound is mapped to bank 73, instrument 6 (Harpsichord); the "boing" sound is mapped to bank 73, instrument 103 (FX 8); the "flute" sound is mapped to bank 73, instrument 73 (Flute). (There is also a GM version with the same mappings in bank 0.) jsPlayCommand recognizes these instruments in bank 73 as "HyperFlute", "HyperBoing", and, naturally, "Hypsichord". You can load these in your synthesizer and your play commands will sound just like they did in HyperCard!

* MIDI (1982) is actually older than HyperTalk (1987). Neither is all that modern.