Html5bytebeat
Bytebeats in HTML5
Install / Use
/learn @greggman/Html5bytebeatREADME
HTML5 Bytebeat
Bytebeat is the name of type of music made from math.
You provide a function who's only input is time t and from that write some code to generate a sound.
In this particular case t is an 8000hz timer that counts up. For example
sin(t) * 127 + 127
You can choose traditional bytebeat where the output of your function is expected to be 0 to 255 or you can choose floatbeat where the output is expected to be -1 to +1.
Functions are just plain JavaScript though sin, cos, tan, floor, ceil and int will automatically be converted to Math.sin, Math.cos, Math.tan, Math.floor, Math.ceil, and Math.floor respectively.
Click here to try your hand at Bytebeat.
Instructions
Modes
There 2 modes
- bytebeat: Your expression is expected to generate byte values from 0 to 255
- floatbeat: Your expression is expected to generate float values from -1.0 to 1.0
Expression Types
- Infix: Standard expressions eg. "
(t * 2) / 4" - Postfix(rpn): Reverse Polish Notation eg "
t 2 * 4 /" - glitch: glitch format or glitch urls.
- function: Return a function. eg. "
return t => (t * 2) / 4"
Infix is standard JavaScript so all Math functions are available. Most math functions you can drop the "Math." part. In other words
sin(t)
is the same as
Math.sin(t)
Postfix requires that each element have at least one space between it.
t2* // BAD!
t 2 * // Good!
If you're unfamiliar with postfix see below
Glitch is a format used by glitch machine for sharing. Examples
- <a href="https://greggman.com/downloads/examples/html5bytebeat/html5bytebeat.html#t=0&e=2&s=8000&bb=5d0000010060000000000000000017e07c68347275b48b8a3e4ae5f2fcf118694f3c5b765c81e4a571018d893ec35ab1c80e51eaadbdb73dbe8af51bf1324d5fc68ca278d3c5fbff8e137344d360a109d789a07673a414f2b2171c4d5de9f1d3f2ecc410e9dfff85520000">42_forever!a13880fa400he!a5kma6kn40g!aCk28!a12k1ld!2fladm!43n</a>
- <a href="https://greggman.com/downloads/examples/html5bytebeat/html5bytebeat.html#t=0&e=2&s=8000&bb=5d000001006b000000000000000017e07ce06937cec415be16c16cd1dc7770bc5386546efdb66845ee0aa7b98eff2ba6e3f457299da2037f6a5ad9e8e38b3611b90fee512d353d0b25fc253191ae89a30e0f7adabb03c956d89bd548392fe27fe4ffe962f8939a5a65ff945a0000">pipe_symphony!aEk5h5f!a11k2h!a9k3hdf!aDk4hg!ad4e!p5fm!a11k2h1rg!a5kdm</a>
These can be prefixed with "glitch://". For example
- <a href="https://greggman.com/downloads/examples/html5bytebeat/html5bytebeat.html#t=0&e=2&s=8000&bb=5d0000010074000000000000000017e07ce661316231c4f949bffc8dd7660451a2571363cac6697ec8fb0cdb5eb5685ae9f83ad7be74c020fa1efe54e1b53cf571811b40ca41357fd42f73eb9683e086bd4aaf30dc675ee6bb090c9840be59402238c6553b9cc0eea0bdc6ddeac83341fdff033c0000">glitch://sadglitch!4.4.9.8.9.6.4.2!aoCk8hq!ad2d!aFk3h1fe!p5d3em!a63hm!a7kFFlp80slf</a>
<a href="https://github.com/erlehmann/libglitch/tree/master/tracks">There's a bunch more here</a>. I have a feeling there's a bug or 2 left for full glitch support
Function
Expects a function body vs infix which expects an expression
infix: sin(t)
function: return t => sin(t)
Note thought that "function" receives t in seconds, not samples.
Postfix
Postfix in this case I guess can be described as forth like. It works with a stack. Each command either adds things to the stack or uses what's on the stack to do something. For example
123 // pushes 123 on the stack stack = 123
456 // pushes 456 on the stack stack = 123, 456
+ // pop the stop 2 things on the stack
// adds them, puts the result on the
// stack stack = 569
Note the stack is only 256 elements deep. If you push 257 elements it wraps around. Similarly if you use pick
with a large value your pick will wrap around. The stack is neither cleared nor reset on each iteration
of your function. Some postfix based bytebeat songs take advantage of this where each iteration leaves
things on the stack for the next iteration.
operators
The postfix operators are
>, < ,=
These take the top two things from the stack, do the comparision, then push 0xFFFFFFFF if the result
is true or 0x0 if the result is false. Think of it has follows: If the TOP thing on the stack is >, <, or = to
the next thing on the stack then 0xFFFFFFFF else 0x0
drop
removes the top thing from the stack
dup
duplicates the top thing on the stack.
swap
swaps the top 2 things on the stack
pick
pops the top thing from the stack and duplicates one item that many items back. In other words
if the stack is 1,2,3,4,5,6,7,3 then pick pops the top thing 3 and duplicates
the 3rd thing back counting from 0, which is no 4. The stack is then 1,2,3,4,5,6,7,4.
Another way to look at it is dup is the same as 0 pick.
put
sets the n'th element from the top of the stack to the current top. In other words if the stack is
1,2,3,4,5,6,7,3,100 then put will pull the top 100 and then set the 3 element back. The stack
will then be 1,2,3,4,100,6,7,3.
abs, sqrt, round, tan, log, exp, sin, cos, tan, floor, ceil, int
min, max, pow
These operators all pop the top value from the stack, apply the operator, then push the result on the stack
/, +, -, *, %, >>, <<, |, &, ^, &&, ||:
These operators pop the top 2 values from the stack, apply the operator, then push the result. The order is as follows
b = pop
a = pop
push(a op b)
In other words 4 2 / is 4 divided by 2.
~
Pops the top of the stack, applies the binary negate to it, pushes the result.
Function
See Rant.
"function" means you could write code that returns a function. The simplest example might be
return function(t) {
sin(t);
}
or shorter
return t => sin(t);
The point being you can write more generic JavaScript. For example
const notes = [261.62, 329.628, 391.995, 523.25, 391.995, 329.628, 261.62, 261.62, 1, 1];
function getNote(t) {
const ndx = (t * 4 | 0) % notes.length;
return note = notes[ndx];
}
return function(t) {
const note = getNote(t);
return sin(t * 10 * note);
}
But see Rant why this is seems kind of missing the point.
Stereo
You can emit an array with 2 values for left and right channels. Eg.
[sin(t), sin(t / 2)]
Extra
Comments can be both // or /* */ style and I'd personally suggest you use comments for your name, the song's name, etc...
There are several extra inputs available:
The mouse position is available as mouseX and mouseY
sin(t * mouseX * 0.001) + cos(t * mouseY * 0.003)
The size of the window is available width and height
Also note, using the comma operator you can write fairly arbitrary code. See this example.
Putting a comment in form of
// vsa: <url>
Will apply a vertexshaderart piece. Example
Rant
The original bytebeat, or at least the one I saw, was fairly simple. 8bits, stack based, few options. When I built a live JavaScript version I just thought "you get an expression that takes time and returns a value". The end.
A few obvious additions, at least to me, were floatbeat, because the Web Audio API itself
takes floats and IIRC some original C based thing that took a function expected floats.
In fact I wrote that first. I just fed an expression that returns floats
into the Web Audio API. I then manually converted a few beatbyte expressions by just
putting (original-bytebeat-expression) / 127 - 1.
The reason I didn't just stick with floatbeat is bytebeat expressions already existed and wanted people to be able to use them without having to understand how to convert, even though it's trivial.
But now I see people have added signed bytebeat. What is the point? Any signed
bytebeat can be turned in to regular bytebeat by just putting + 0x80 at the
end of your expression. The entire point of bytebeat is to be self sufficient,
to put what you need in the expression itself.
I then found a funcbeat in which instead of an expression you pass it a
function body. AFAICT the advantage is you can write code and declare
other functions and data vs having to squeeze everything into an expression with
commas. For example:
const notes = [261.62, 329.628,
Related Skills
docs-writer
99.6k`docs-writer` skill instructions As an expert technical writer and editor for the Gemini CLI project, you produce accurate, clear, and consistent documentation. When asked to write, edit, or revie
model-usage
342.0kUse CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.
arscontexta
2.9kClaude Code plugin that generates individualized knowledge systems from conversation. You describe how you think and work, have a conversation and get a complete second brain as markdown files you own.
cursor-agent-tracking
134A repository that provides a structured system for maintaining context and tracking changes in Cursor's AGENT mode conversations through template files, enabling better continuity and organization of AI interactions.
