Modding Guide

From BeepBox Wiki

If you're a BeepBox "power user" and you know how to program, making a mod may be in your interest - maybe even if you don't know how to program, though that will make the process more difficult than it should be, of course.

In this article we will look at how BeepBox and its family of mods work from a programming perspective, and go over some examples of how to change, extend, or add features in the program.

This guide is written assuming that you know how to program to some degree - even a very basic look at how to program in general would take many articles like this one to be reasonably helpful. If that's not the case, you might still be able to contact a developer and make suggestions, or ask for help with developing a mod - given that most (if not all) of these are worked on as a hobby, you may very well be able to add to the fun of it even if you can't directly contribute code.

You should be aware that working on a published mod may be a rather daunting task at times, especially if it sees substantial usage. There's some tricky problems to solve (such as maintaining backwards compatibility, aligning your work with user expectations set by other software, endless user-facing design questions, ...), enough that they may make the whole process feel like a (usually unpaid) job. Though maybe that's part of the fun - working on software with many users can be quite different from one-off scripts that don't leave your computer.

Setup

BeepBox is a web application, accessible via the website located at https://www.beepbox.co.

Unlike how web applications are usually expected to work, all of its functionality is contained in the client side (or "frontend") - a "backend" HTTP server is only used to transfer the program to the client, and nothing else. An important consequence of this is that if you download all the code sent to your browser, you can use BeepBox even if you don't have any internet access! The "offline version" linked at the bottom of the program description is built upon this - it's one giant .html file with (mostly) everything needed inside.

Given that, mod development involves at least HTML, CSS, and JavaScript, with most work being on the JavaScript side.

This video tutorial can help with the setup if this guide is too confusing...

The simplest setup

A way you can start playing around with the code is to download the source code of this HTML page (probably saved with a filename of index.html), and the unminified JavaScript code, which can be found here. Normally this file will be used instead, which is not meant to be human-readable. Of course, whatever filename you end up using for the JavaScript file has to be used also in the HTML page you saved - as is, it will look for a beepbox_editor.min.js file, but you probably will want to name yours beepbox_editor.js, or really anything else you want, so you'll have to edit the .html file so that it uses the different filename you've picked.

The actual setup

The immediacy of opening those files in a text editor, making a few changes, saving, then opening (or reloading) the HTML page is nice for quick experimentation. This is not how BeepBox and some mods are actually developed, though. If you look at the source code linked in the program description, you may notice that there's a lot more going on in there. The main difference is that an additional "compilation step" exists, which produces the giant .js file seen above. This "compilation step" involves the following:

  1. The compiler for the TypeScript programming language is used to turn .ts source code files into .js compiled files
  2. The compiled .js files are combined into one giant .js file via a bundler (currently, Rollup)
  3. A minifier (currently, Terser) is used to make the giant .js file smaller (and unreadable, though that's normally just a side-effect), to minimize the amount of data that needs to be sent from the server to the client

All of those tools can take quite a bit of time to execute, standing between your changes and your ability to run your new code in the browser, which may be a bother, but each tool is used for a good reason (to catch more mistakes before you run the code, to avoid making many HTTP requests, to transfer less data over the wire, respectively).

This entire process is automated with some Bash scripts: one of them can be seen here. Bash scripts can be run directly in macOS or in most Linux distributions, but on Windows you will need additional software which you may not have installed, so there's additional setup needed if you're on that operating system.

The tools you need to work with the source code ideally are:

  • Git: used to manage the source code as it changes over time. BeepBox's git "repository" is hosted on GitHub.
    • Windows users probably will want to install Git for Windows, it will come with an emulation environment set up to run the Bash shell, where the Bash scripts included in the repository can be executed.
  • Node.js: This is the environment the TypeScript compiler will run in. Also, the npm package manager will come with it. That, after running npm install in the folder where you've "cloned" the BeepBox git repository to, will grab the dependencies listed in the included package.json file. For BeepBox that will install:
    • TypeScript
    • Terser
    • Rollup (and some plugins)
  • A programming-centric text editor (or even, an IDE). Visual Studio Code is a popular choice these days (relevant to BeepBox, it comes with a fancy TypeScript integration, which makes it easier to navigate the source code, view compiler errors, type information, among several other features), but many editors exist. Use what feels best!

If you plan on publishing source code, or even an hosted version of your mod, then you may need additional software in order to, for example, "push" code to GitHub (in particular, GitHub Desktop may be the easiest option for that).

Once you have those tools installed, you should be able to run the following, in a Bash shell:

$ # Download the source code
$ git clone https://github.com/johnnesky/beepbox
$ # Move inside the folder where the source code was downloaded to
$ cd beepbox
$ # Install dependencies
$ npm install
$ # Run the entire compilation process
$ npm run build

If everything goes well, you should be able to then run your compiled version of BeepBox by opening the website/index.html (or website/index_debug.html) page in your browser of choice. Then, after any change you make in the .ts files, you must run npm run build again for the changes to show up in your browser.

If you want to start development on top of an existing mod instead, the setup process will resemble either one of those two methods: working with the compiled JavaScript file, or working with the TypeScript source code. But a mod may introduce its own additional setup burdens - check if a README file is included, it may contain the help you need.

Setting up programming tools can be very cumbersome - you should contact a mod developer if you have any trouble. Hopefully everything went well, though.

Now what? Well, anything you want!

Where to start

Most likely, the easiest thing to do first is to follow what started this modding business in first place - changing the values of the fields in the Config class (which in the TypeScript source can be found here, and in the compiled JavaScript should usually be found at the top of the beepbox_editor.js file) to raise limits, use different "chip waves"... Keep in mind however, that not everything is as simple as changing a number there. The rest of the code may be written assuming a certain range of values, without this being a documented requirement, so you'll break everything. But don't be afraid of trying things out - making sure that you keep a copy of the previous version of the code (doing this extensively is the reason git is used), you can simply revert to what you had before the ill-fated change.


How to find where to make a change?

It depends. It may be simplest to look at the UI first, optionally looking at the "generated HTML" via your browser's development tools. Things like class names, IDs, or other such attributes of the HTML elements can help narrow down your searches. "Generated HTML", because some of it is created as the .js code executes (using the imperative-html npm package, which was spun off from BeepBox), instead of being defined "statically" in the website/index.html file.


For example, where is the dropdown for the song's scale located in the code? Let's find out.

One thing to try is to run a search operation over multiple files in a folder (Visual Studio Code can do this, for example), with any text you see on the screen. Make sure you only search through the source code, however - most of the time you only need to look at the .ts files.

Merely searching for Scale yields too many results. We can narrow them down by searching for Scale:, or even more by searching for Scale: if it appears near a span element with class tip, as seen in the generated HTML:

<div class="selectRow">
  <span class="tip">Scale:</span>
  <div class="selectContainer">
    <select>
      ...
      <option value="11">expert</option>
      ...
    </select>
  </div>
</div>

Trying that out, we find the only occurrence:

$ grep -r 'span' {editor,synth,player} | grep 'Scale:'
editor/SongEditor.ts: span({class: "tip", onclick: ()=>this._openPrompt("scale")}, "Scale:"),

(if you want to know more about grep you can look here, but you don't have to...it's a program to search for text in a file like many others)

This is only the label close to the dropdown. The dropdown (a <select> element) is below that:

div({class: "selectRow"},
  span({class: "tip", onclick: ()=>this._openPrompt("scale")}, "Scale:"),
  div({class: "selectContainer"}, this._scaleSelect),
),

In this case, it's that this._scaleSelect part we're interested in. Most of the time, this will refer to an instance of the class we're currently defining, whether we're inside of a class method (not static, those belong on the class), or elsewhere in other class properties. So in this case, we're looking for a _scaleSelect property inside the class we've found ourselves in. We can search backwards from here, looking for the pattern class ArbitraryName (maybe class [a-zA-Z0-9_]+ if your search tool supports regular expressions), where we can find this:

export class SongEditor {

Which means we're defining the SongEditor class currently. Then, searching forward from there for _scaleSelect, we can find this:

private readonly _scaleSelect: HTMLSelectElement = buildOptions(select(), Config.scales.map(scale=>scale.name));

The select() function call will generate a <select> HTML element (via the imperative-html package mentioned above), which is what is stored in the _scaleSelect property. The Config.scales.map(scale => scale.name) portion will return the name property of all objects contained in Config.scales as an array (see also the Array.prototype.map documentation), and both of those are given to buildOptions, which will populate the <select> (first argument) with <option> elements corresponding to the values in the array (second argument), where their value attribute will be the index of the array value, and their label will be the array value itself.

If you wanted to, for example, change the scale names, then looking at all this wasn't necessary, and you probably already found that from following the first suggestion to simply play around with the values in the Config class (though note that if you do that, you could break the rest of the code! See this one example that relies upon the "expert" scale having that specific string as its name property). However, if you wanted to change the HTML layout, or CSS styling, or some other logic, then that's where you'd want to start looking. If you search for the other occurrences of _scaleSelect you can find out where other things happen such as where the realName property is used, or where the selected scale is applied to the song.

Sometimes, it's not as simple as looking "from the outside in". In that case, you'll have to read more of the code and try to piece together an overview of what's going on, or ask someone who has done that work already. The sections below may also help you quickly find where to look.

Source code layout

(based on this commit)

Filename Description
.gitignore Used to specify files that aren't supposed to be tracked with git.
LICENSE.md A copy of the MIT license, which is what the BeepBox source code is licensed under.
README.md Has a description of the project, install instructions, etc. The text is marked up using Markdown.
compile_beepbox_editor.sh Bash script that turns the TypeScript source code (using editor/main.ts as the entry point) into beepbox_editor.js and beepbox_editor.min.js.
compile_beepbox_player.sh Bash script that turns the TypeScript source code (using player/main.ts as the entry point) into beepbox_player.js and beepbox_player.min.js.
compile_beepbox_synth.sh Bash script that turns the TypeScript source code (using synth/synth.ts as the module to export as a library) into beepbox_synth.js and beepbox_synth.min.js.
deploy.sh Bash script that runs the entire compilation process, and deploys a Google App Engine project using the gcloud CLI. It's unknown whether this is still in use.
package-lock.json Lockfile used by npm.
package.json Package definition used by npm. This file specifies what dependencies you'll get by doing npm install, and contains the definition of some shortcut commands to run the compilation process, for everything (npm run build), or only for the editor (npm run build-editor), for the player (npm run build-player), or for beepbox_synth.js (npm run build-synth).
tasks.txt To do list. Not used anymore.
tsconfig.json TypeScript compiler options, used when creating beepbox_editor.js.
tsconfig_player.json TypeScript compiler options, used when creating beepbox_player.js
tsconfig_synth_only.json TypeScript compiler options, used when creating beepbox_synth.js. The two compiler option files above "inherit" all the actual compiler options from this one.
editor/ArrayBufferReader.ts Class that exposes a stateful reading interface to DataView, keeping track internally of a "read position". Only really used for importing MIDI files.
editor/ArrayBufferWriter.ts Class that exposes a stateful writing interface to DataView, keeping track internally of a "write position". Only really used for exporting MIDI files.
editor/BarScrollBar.ts When you have enough bars, a scrollbar will appear to let you navigate around the "timeline". This file contains the implementation of that scrollbar.
editor/BeatsPerBarPrompt.ts Code to display the prompt for the "Change Beats Per Bar..." edit action. The actual implementation of this action lives in the ChangeBeatsPerBar class.
editor/ChangeNotifier.ts A simple class that lets one expose a "notifier", which only signals to the "watchers" that a change has happened, but no other information is given. changed must be called in order to trigger a change notification.
editor/changes.ts Most Change subclasses (with the code behind edit actions that must be persisted in the undo history) live here.
editor/Change.ts The Change class and some other variations on that interface live here.
editor/ChannelRow.ts Contains the implementation of a pattern row (for clarity, this is not related to PatternEditor.ts, but rather TrackEditor.ts).
editor/ChannelSettingsPrompt.ts Code to display the "Channel Settings" prompt. The implementation of the song data changes controlled here live in changes.ts.
editor/ColorConfig.ts The CSS for the themes available lives here.
editor/EditorConfig.ts Similar to SynthConfig.ts, but editor-centric. Instrument presets are defined here.
editor/EnvelopeEditor.ts Contains the implementation of the "Envelopes" section of the instrument settings.
editor/ExportPrompt.ts Code to display the prompt seen by clicking on "File > Export Song...". The MIDI/MP3/WAV export implementations also live in this file.
editor/FadeInOutEditor.ts Contains the implementation of the "Fade In/Out" section of the instrument settings.
editor/FilterEditor.ts Contains the implementation of the "EQ Filter" and "Note Filter" sections of the instrument settings.
editor/HarmonicsEditor.ts Contains the implementation of the "Harmonics" section of the instrument settings.
editor/ImportPrompt.ts Code to display the prompt seen by clicking on "File > Import Song...". The MIDI import implementation also lives in this file.
editor/KeyboardLayout.ts Code to translate key presses into note pitches, based on the layout selected in the "Note Recording Setup" prompt.
editor/LayoutPrompt.ts Code to display the prompt seen by clicking on "Preferences > Choose Layout...".
editor/Layout.ts The CSS for the "long" and "tall" layouts lives here.
editor/LoopEditor.ts The implementation of the draggable song loop points lives here.
editor/main.ts Entry point for the song editor.
editor/MidiInput.ts Contains code to integrate the Web MIDI API with the "Note Recording" feature.
editor/Midi.ts Contains some MIDI-related constants and helper functions.
editor/MoveNotesSidewaysPrompt.ts Code to display the prompt for the "Move All Notes Sideways..." edit action. The actual implementation of this action lives in the ChangeMoveNotesSideways class.
editor/MuteEditor.ts Code for the mute buttons (seen via the "Enable Channel Muting" preference) lives here.
editor/OctaveScrollBar.ts The implementation of the scrollbar seen via the "Show Octave Scroll Bar" preference lives here.
editor/PatternEditor.ts Contains the implementation of the editable piano roll.
editor/Piano.ts The implementation for the widget seen via the "Show Piano Keys" preference lives here.
editor/Preferences.ts Contains code to load and save BeepBox's preferences to localStorage.
editor/Prompt.ts Exports the Prompt interface and nothing else.
editor/RecordingSetupPrompt.ts Code to display the prompt seen by clicking on "Preferences > Set Up Note Recording...".
editor/Selection.ts Contains code related to both piano roll and pattern selections. Also contains the copy and paste implementation for those two types of selections.
editor/SongDocument.ts Class that maintains the undo history, along with other song-related state data for the editor.
editor/SongDurationPrompt.ts Code to display the prompt for the "Change Song Length" edit action. The actual implementation of this action lives in the ChangeBarCount class.
editor/SongEditor.ts BeepBox's overall editor code lives in this file, though a lot of its subparts are located in other classes/files.
editor/SongPerformance.ts Mostly contains code related to the "Note Recording" feature, though the piano roll, and the keys on its left, if enabled, also call a few methods in this class to support note audio "previewing".
editor/SongRecoveryPrompt.ts Code to display the prompt seen by clicking on "File > Recover Recent Song...".
editor/SongRecovery.ts Contains the code to save a song for later recovery, via the prompt shown above.
editor/SpectrumEditor.ts Contains the implementation of the "Spectrum" section of the instrument settings.
editor/style.ts Most of BeepBox's CSS lives in this file.
editor/TipPrompt.ts Code and text for all of the purely informational prompts seen when clicking on some section labels live in this file.
editor/TrackEditor.ts Contains the implementation of the area where you edit the overall "timeline" (involving bars, channels, and patterns) for BeepBox songs.
player/main.ts Entry point for the song player.
synth/Deque.ts A double-ended queue implementation. Mostly used to keep track of Tone instances in the synthesizer.
synth/FFT.ts Code for computing discrete Fourier transforms (generally using the "fast Fourier transform" algorithm) lives here. BeepBox only really uses inverseRealFourierTransform, which takes frequency-domain data and turns it into time-domain data, to implement the Harmonics, Picked String and Spectrum instruments.
synth/filtering.ts Code to compute the coefficients of audio filters (such as low-pass, high-pass, peak, ...) lives here. The application of those filters, as elaborated upon in the comments in this file, is located elsewhere, as needed.
synth/SynthConfig.ts Of greater importance, the Config class lives here, where a lot of important constants can be found. Also includes some important type definitions, and a few miscellaneous functions such as the one where the noise channel waves are computed.
synth/synth.ts BeepBox's synthesizer lives here. Most of the code that directly affects the audio you hear can be found in this file.
website/2_3/beepbox_editor.js Compiled code for BeepBox 2.3.
website/2_3/beepbox_editor.min.js Compiled code for BeepBox 2.3, minified.
website/2_3/beepbox_offline.html Offline version of BeepBox 2.3.
website/2_3/index.html Web page used as the "entry point" by the browser for BeepBox 2.3.
website/3_0/beepbox_editor.js Compiled code for BeepBox 3.0.
website/3_0/beepbox_editor.min.js Compiled code for BeepBox 3.0, minified.
website/3_0/beepbox_offline.html Offline version of BeepBox 3.0.
website/3_0/index.html Web page used as the "entry point" by the browser for BeepBox 3.0.
website/3_0/player/beepbox_player.js Compiled code for BeepBox 3.0's song player.
website/3_0/player/beepbox_player.min.js Compiled code for BeepBox 3.0's song player, minified.
website/3_0/player/index.html Web page used as the "entry point" by the browser for BeepBox 3.0's song player.
website/apple-touch-icon.png Used on iOS when adding BeepBox to the home screen.
website/app.yaml Google App Engine settings.
website/beepbox_offline_template.html File used to create beepbox_offline.html, which is done in compile_beepbox_editor.sh.
website/browserconfig.xml "Pinned site" configuration file, for Internet Explorer.
website/competition/index.html Web page used as the "entry point" by the browser for the competition version of BeepBox.
website/competition/player/index.html Web page used as the "entry point" by the browser for the song player of BeepBox's competition version.
website/favicon.ico Used as the icon in browser tabs, bookmarks...
website/icon_32.png 32x32 favicon.
website/icon_maskable_192.png Maskable icon, used with the progressive web app definition in manifest.webmanifest.
website/icon_shadow_192.png App icon, used with the progressive web app definition in manifest.webmanifest.
website/icon_windows_150.png When BeepBox is turned into a "pinned site" with Internet Explorer, this is the icon used.
website/index_debug.html Web page used as the "entry point" by the browser for BeepBox. This version is slimmed down compared to index.html and loads beepbox_editor.js, which is not minified, and thus should be a bit more helpful when investigating errors.
website/index.html Web page used as the "entry point" by the browser for BeepBox.
website/manifest.webmanifest Manifest file defining a progressive web app.
website/player/index.html Web page used as the "entry point" by the browser for BeepBox's song player.
website/robots.txt Specifies what pages a web crawler should visit, and which ones it shouldn't.
website/service_worker.js A service worker, only really used to set up some client-side caching.
website/synth_example.html Web page used as the "entry point" by the browser for an example integrating BeepBox's synthesizer as a JavaScript library.


Your clone of the source code will naturally contain a few more files, such as:

  • The ones containing the dependencies installed by npm, in the node_modules folder
  • git's internal metadata, in the .git folder, which, because of the leading dot, may be hidden in your file browser of choice
  • The .js files generated by the TypeScript compiler, in the build folder, later mangled by the bundler and minifier (in the website folder).


Before you publish a mod

Pick a unique name

Of course. One instructive example of a confusing non-unique mod name is what is referred to in this wiki as Thurmbox - it's sometimes called Pandora's BeepBox, Pandora's JummBox, Pandora's Box + Sample Library, etc, which collided with PaandorasBox.

Consider making your song data distinct in an unambiguous way

For URLs, what you could do is follow the example set by JummBox (and its mod family), where a single character is used to denote a "variant" (another name for a mod). When a song URL doesn't have this character, it's assumed to come from BeepBox.

The variants already in use are:

Mod Character In hexadecimal format
JummBox j 6A
GoldBox g 67
UltraBox u 75
BeepBox b 62
Dogebox2 d 64
AbyssBox a 61
Midbox m 6d
Slarmoo's Box s 73
Unbox U 55
VoxBox v 76

For JSON files, you probably should at least change this _format property in Song, to the name of your mod.

Not all mods do this. Many of them simply extend the format already in use by the codebase selected as a starting point. Supporting multiple song data formats can still be done even when a scheme like the above is not followed, but more information will be needed to disambiguate between songs made in different programs.

Publishing your mod

In most cases, hosting a mod is as simple as uploading one .html file and one .js file (both taken from the website folder, if you're working with the TypeScript source code) to a web server, though of course if there are more dependencies, you have to upload all of those as well.


Tips

  • Use website/index_debug.html (as seen in the source code layout section), which loads the unminified compiled code. Errors should be a bit more helpful - pointing to actual class names, for one.
  • Most of the time, you'll be working with the editor part of the code, so you can run
    $ ./compile_beepbox_editor.sh
    
    in the BeepBox source code folder. That should be a bit faster than compiling the editor, the player, and the beepbox_synth.js file, but if you need those, then you will need to either also run the corresponding Bash scripts, or just run npm run build anyway.
  • Get in the habit of using a graphing calculator, like Desmos. It's much easier to spot anomalies or other interesting data points that way.