Introducing AudiOEIS - The OEIS Audio Sequencer
Background
The Online Encyclopedia of Integer Sequences (OEIS) is an iconic institution of the Internet. It is the definitive and de-facto resource for all things related to integer sequences, which often crop up in a lot of places across mathematics. Pretty much all sequences of any note are recorded by the OEIS, and it can be an elucidating tool for making connections across areas of math. Some of my favorite entries include:
- The Fibonacci Sequence (A000045)
- Pascal’s Triangle (A007318)
- Look and Say sequence (A005150)
- Period-doubling sequence (A096268)
The database hosts way more content than just integer sequences. There are several interactive “demos” which allow users to explore sequences in surprisingly different contexts. In fact, the website has a “music” link, which lets the user “listen” to a chosen sequence. This sound is produced by a browser-based tool that generates a MIDI audio file from an initial sequence seed.
I noticed that this tool is for listening to “one-off” sequences, and cannot gracefully support multiple tracks like a typical Digital Audio Workstation (DAW). Inspired by this tool to extend the underlying concept of “hearing sequences”, I set off to create something that satisfied my new requirements.
Specifically, I desired more control over the sound and ability to mix sequences, so I whipped up a quick app that provides a DAW-style control panel for fetching, replaying, and modifying sequences and the resulting soundscape. Here’s a link to the final product:
audioeis.live

Design
I’m sort of cheating by calling this an app, in the sense that there is no real “back end” to this tool. The sound synthesis component happens entirely client-side within the user’s browser through some clever JavaScript.
As a result, there is no persistent memory or storage with this app. In other words, If you do not save your session to a WAV file, it’ll be forever lost to the ether. Personally, I consider this ephemerality a “feature” to keep things simple and relatively easy to maintain. Not to mention it greatly simplifies any future issues I may encounter with long-term storage, access control, or security. Nefarious actors can’t steal what doest exist, after all.
Essentially, the app is a wrapper around the OEIS API, and is hosted entirely in Cloudflare using their Worker feature. The only “dynamic” piece is a small snippet of JavaScript that actually makes the call to OEIS API, which was necessary to get around pesky browser-based origin controls.
Audio Controls
I wanted to keep things somewhat simple, while still providing the user with enough parameters to experiment freely. Sound synthesis is produced through a call to the createOscillator() function, exported by the BaseAudioContext interface. There are four basic oscillator types supported for the waveform (sine, triangular, sawtooth, square).
For performing the actual mapping between integers and frequencies, I took three separate approaches in an attempt to cover all the use cases I could think of:
- MIDI: The most straightforward concept, and the one that OEIS currently uses. Each integer is mapped to a MIDI note (modulo 128)
- Diatonic: To try to force these sequences into a more melodic timbre, it “squeezes” the sequence terms into a well-define scale, but may destroy some of the sequence’s original information as a result. There is support for each major/minor mode, and you can specify the basis “root note” for each scale.
- Microtonal: The ratio of successive terms produces the frequency of the next note to play. Since these ratios are likely bizarre and wont conform the standard diatonic scale, I’ve dubbed it “microtonal”. Its probably the most interesting mode from a mathematical and musical perspective.
To further elucidate the mappings, check this snippet of relevant code from the source repo:
function clamp(v, lo, hi) {
return Math.max(lo, Math.min(hi, v));
}
function midiToFreq(midi) {
return 440 * Math.pow(2, (midi - 69) / 12);
}
...
function mapMidi(n) {
return midiToFreq(Math.abs(n) % 128);
}
function mapDiatonic(n, rootNote, scale) {
const len = scale.length;
const degree = ((n % len) + len) % len;
const octave = Math.floor(Math.abs(n) / len);
const midi = clamp(rootNote + scale[degree] + octave * 12, 12, 120);
return midiToFreq(midi);
}
function mapMicrotonal(terms, index, rootHz) {
const a = terms[index];
const b = terms[index + 1];
if (b === undefined) return rootHz;
const safeA = a === 0 ? 1 : Math.abs(a);
const safeB = b === 0 ? 1 : Math.abs(b);
let ratio = safeB / safeA;
if (ratio > 4) ratio = 1 + Math.log2(ratio) / 4;
else if (ratio < 0.25) ratio = 1 / (1 + Math.log2(1 / ratio) / 4);
return clamp(rootHz * ratio, 27.5, 4186);
}
...
Each sequence is treated as a separate “track” by the DAW, giving the user greater control over an individual sequence’s presence in the overall mix. When you are satisfied, you can export the track to a WAV file for post-processing.
Heres a look at how the site responds when composing a track with several sequences at once:

Deployment and Maintenance
This project was also a small experiment for myself to practice some proper continuous integration and deployment (CI/CD). I’ve hosted the source code on my GitHub in a public repo. Any new commits to this repo will kick off an automated build & deployment process that pushes the update to the Cloudflare Worker node automagically.
I originally overcomplicated this project, thinking I needed two nodes (one for the app, and one to fetch the sequences from the API) but found I was mistaken. You can probably still find artifacts from that prior architecture in the git commit history, so please don’t judge me. Turns out Workers are a lot more versatile than I expected!
Cloudflare Analytics
Since I am using Cloudflare to proxy requests from the app to the OEIS API, I already get a ton of decent analytic information “for free”. I’m mostly interested in seeing how many active users the tool actually gets, as well as tacitly monitor uptime for the app.
For the latter point, I configured Cloudflare to send a webhook to a Discord in the event that an outage is detected. Fingers crossed that I never see a ping!
Conclusion
Heres the final link (again) to the site which should (hopefully) be live at the time of this post:
If you’ve stumbled across this blog or the app in your travels and like what you see, I hope you enjoy and share with another fellow traveler. Cheers!