Galois Field Orbits and Shift Registers
I have been trying to generate musical-sounding rhythms and melodies using Galois Fields. Euclidean (algo)Rhythms are frequently used in electronic music, so there is some prior art for mathy things producing musically interesting results. In \(GF(2^n)\) (the Galois Field of \(2^n\) elements), the field elements are bitstrings, which map naturally to gate sequences. Since we can do math over field elements, we can do math over sequences. Maybe we can combine sequences in exciting ways.
Most of what I've tried with fields so far hasn't been interesting, but one thing produced a series of decent-sounding "shifts" of the same sequence, which I found surprising. We need two things to understand this: a way to write rhythms as zeros and ones, and a way to treat those same zeros and ones as elements of a Galois Field.
Ingredients
Gates and Sequences
In electronic music, sequences of "gates" express rhythms. A gate is either "open" or "closed." An "open" gate says "play this instrument at this step" and a "closed" gate says "play nothing at this step." A gate sequence is a bunch of gates in a row, like "open closed open open", which would mean the associated sound is played, skipped, played, played (repeating forever).
If we use 1 for "open" and 0 for "closed," we can write sequences compactly.
For example, we can write a kick (bass drum) pattern of 1000, a snare pattern of 0010, and a hihat pattern of 0101.
Read these one digit at a time, left to right: 1000 is "one zero zero zero", not "one thousand".
Drawn out, those would be:
Here's what that pattern sounds like:
These 0101.. bitstrings look an awful lot like binary numbers!
Galois Fields
I'll attempt to explain Galois Fields well enough here to follow the rest of the post, but I recommend reading Learn you Galois Fields for Great Good if you'd actually like to learn how they work.
When people talk about math, they are typically discussing numbers and operations. The most familiar operations are addition, subtraction, multiplication, and division. These operations take (usually) two numbers and give you back a new one.
We've come up with a bunch of rules that these operations follow.
One such rule is the "distributive property," which says: x × (y + z) = x × y + x × z.
Sometimes we change the way the operations work.
When doing "clock math" (math with time), numbers past 12 "wrap around", so 6+7 = 1.
Wrapping is the same as keeping the remainder: 13 ÷ 12 leaves a remainder of 1.
The number we wrap around, 12 in this case, is called the modulus, and this style of math is called modular arithmetic.
Math (for some definition of math) still works on a clock face.
If we can change what operations do, can we also change what numbers are? Maybe; we could, for example, replace numbers with drum patterns, then define specific operations over these patterns that allow us to "add patterns together." Imagine "adding" two patterns together with a simple rule:
In each output position, the gate is open (1) only when the gate in the same position was open in exactly one of the two input patterns.
Working that out for a single position, we get this table:
| input 1 | input 2 | output |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
Then, for a full sequence, we'd say: 0110 + 1101 = 1011 (check this yourself).
We've changed our numbers and changed our operations.
Are we still doing math?
We have a new "add" operation, does it follow any familiar rules?
One familiar way to understand multiplication by a nonnegative whole number N is repeated addition: combine N copies of a value.
But our new things are gate sequences, not whole numbers, so that recipe no longer tells us what multiplication should do.
Can we define multiplication at all?
It turns out that the answer to these questions is sometimes. For some combinations of "things" and "operations," the familiar rules of math keep working (the distributive property, for example). When a combination allows the rules to work, we call it a field. A field built from only a finite collection of things is called a Finite Field, or Galois Field (named after Évariste Galois).
When Clock Math Forms a Field
We can now build a working Galois Field using:
- Numbers: 0, 1, 2, 3, 4, 5, 6 (7 total)
- Operations: Clock-math/modular arithmetic
With a modulus of 7, addition and multiplication work normally, followed by taking the remainder.
Division is a bit trickier.
For division to make sense, 6 ÷ 2 needs to have exactly one answer: "the number that produces 6 when multiplied by 2."
In other words, dividing by 2 is impossible if we can find any A and B where:
AandBare different numbersA × 2andB × 2produce the same answer
When the modulus is 7, this is our multiplication table, with every 6 marked:
| × | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
| 2 | 0 | 2 | 4 | 6 | 1 | 3 | 5 |
| 3 | 0 | 3 | 6 | 2 | 5 | 1 | 4 |
| 4 | 0 | 4 | 1 | 5 | 2 | 6 | 3 |
| 5 | 0 | 5 | 3 | 1 | 6 | 4 | 2 |
| 6 | 0 | 6 | 5 | 4 | 3 | 2 | 1 |
These are the ways to multiply numbers together and get 6:
3 × 2(or2 × 3)6 × 1(or1 × 6)4 × 5(or5 × 4)
Ignoring zero (division by zero isn't defined normally either), the only answer to 6 ÷ 2 is 3.
Work this out for enough values to convince yourself that we can still do division.
So, with a modulus of 7 (or any prime number): addition works, subtraction works (try it! use the addition table), multiplication works, division works. With these basics working, most of normal math works too.
Math Using Gate Sequences
Math over gate sequences turns out to be possible.
Start with the four two-bit sequences: 00, 01, 10, and 11.
They are still sequences, not numbers; 10 is "one zero", not "ten".
We can define addition using the same rule we sketched earlier:
| + | 00 | 01 | 10 | 11 |
|---|---|---|---|---|
| 00 | 00 | 01 | 10 | 11 |
| 01 | 01 | 00 | 11 | 10 |
| 10 | 10 | 11 | 00 | 01 |
| 11 | 11 | 10 | 01 | 00 |
Multiplication is trickier, so you'll have to trust me.
Here's the worked-out table, with every 01 marked so we can check that division still makes sense:
| × | 00 | 01 | 10 | 11 |
|---|---|---|---|---|
| 00 | 00 | 00 | 00 | 00 |
| 01 | 00 | 01 | 10 | 11 |
| 10 | 00 | 10 | 11 | 01 |
| 11 | 00 | 11 | 01 | 10 |
You know the drill.
We can create 01 from:
01 × 0110 × 11(or11 × 10)
You can check the rest of the values if you'd like.
So we have all four operations:
- Addition: a gate is open when exactly one of the two inputs is open (this operation is called XOR)
- Subtraction: exactly the same as addition (try it)
- Multiplication: complicated, but doable
- Division: complicated, but also doable
Nothing here is special about two-bit sequences. For any length \(n\), there are \(2^n\) different sequences, and we can form a field from them. There can be more than one multiplication table that works for a particular \(n\), but the resulting fields are all structurally equivalent (isomorphic).
So, yes! We can replace numbers with sequences, redefine the operations, and have something that works like "normal math". Multiplication of gate sequences is all we need to start producing the strange rhythmic property I've encountered.
Show me how this actually works (optional detour)
Each element is really a polynomial, with the bits as its coefficients.
The leftmost bit is the highest power (but either endian works), so 1011 is:
\[
1 x^3 + 0 x^2 + 1 x^1 + 1 x^0
\]
The modulus is an irreducible polynomial. A given \(n\) might have a few different choices for the irreducible polynomial. We do our operation normally, divide by the modulus (using long division of polynomials), and keep the remainder.
The examples in this post use \(x^2+x+1\) for length two sequences, \(x^3+x^2+1\) for length three, and \(x^4+x+1\) for length four.
Addition and subtraction are just bitwise XOR. Multiplying by \(x\) starts as a left-shift by 1; if the result is too long, we reduce it using the modulus.
Once we have addition, subtraction, and multiplication by \(x\), we can build the "remainder function" by adding shifted copies of the irreducible polynomial until we have the remainder.
With those pieces, we can implement multiplication and division.
Learn you Galois Fields for Great Good goes into much more detail. Read that if this seems interesting to you!
Galois Orbit Sequences
My first idea was to use field elements directly to sequence drums, then find some mechanism to move between sequences and evolve the rhythm. An orbit in a field is the path you get by starting with one field element and repeatedly multiplying by another. We can use an orbit as that mechanism.
In code, that'd be:
def orbit(start, gen):
ret = []
current = start
while current not in ret:
ret.append(current)
current = current * gen # field multiplication
return ret
As long as neither start nor gen is 000, the field is finite, so an orbit will always come back to where it started.
Some choices of gen visit all \(2^n-1\) nonzero elements of the field; others only visit a few of them.
I'll call the first kind a full orbit, since the distinction turns out to matter later.
The orbit using sequences of length 3, gen=010, and start=001 is:
001 010 100 101 111 011 110
It contains seven steps, covering every element other than 000.
Elements as Patterns
Next we'll convert the orbit into drum sequences using each element to sequence a single drum. We play a sequence for a while, then switch to the next sequence in the orbit.
For example, running the kick through one trip around the orbit, playing each sequence one time:
This sounds like:
On its own, it's fun. Each element is 3 steps long, so the full loop has 21 steps and does not naturally line up with the four-step groupings common in 4/4. I enjoy polyrhythmic/odd-metered music, so this is a good start.
Let's add more drums to the beat. We'll give each instrument its own starting element, then play the sequences all together. This looks something like:
And sounds like:
I do not like listening to this beat.
It's too dense, there's nothing obviously repeating to gravitate to; the structure is all buried.
Every instrument also eventually plays 111, which adds more busyness to the beat.
Changing start, or choosing another gen that produced a full orbit, did not resolve the problem.
The orbit visits every possible nonzero three-bit pattern, so this ends up just being dense and chaotic.
Let's try something different.
Bits as Patterns
Let's "transpose" the sequences. For three-bit elements, we'll sequence 3 different instruments. Then, as we move through the orbit, use each bit of each element as the gates for the 3 instruments.
The pattern an instrument plays is defined by the whole orbit, rather than a single element.
Here's a diagram to help this make sense:
This sounds like:
This new beat sounds way better to me. What's going on?
Lining Up the Rhythms
Here are those three rhythms lined up against each other.
kick 0011101 hihat 1001110 snare 0100111
Maybe you can see what is happening. The hihat is playing the same rhythm as the kick, just shifted right by one space. The snare is also the same rhythm, this time shifted right by two.
I tried a bunch of different start values, gen values, and field polynomials. At the time, this property seemed to hold for everything I tried.
For example, with gen=100 (and the same field and start), we get a different full orbit, but the rows are still rotations of each other:
orbit 001 100 111 110 010 101 011 kick 0111010 snare 0011101 hihat 1010011
Here's another one for a larger field (follow the 1111 at the end of the first row):
orbit 0001 0010 0100 1000 0011 0110 1100 1011 0101 1010 0111 1110 1111 1101 1001
rows 000100110101111
001001101011110
010011010111100
100010011010111
Transposing a full orbit always seems to produce a bunch of instrument rows that are just shifts of each other.
This is Strange
Why would the kick's rhythm have any relationship to the snare's?
Look back at the multiplication table for the four-element field.
Consider 01 × 01 = 01 and 11 × 11 = 10.
The inputs to both multiplications end in a 1, but the results 01 and 10 end in 1 and 0 respectively.
Clearly multiplication "mixes" all the bits up somehow.
If multiplication mixes up the entire sequence, how do we end up with the same sequence shifted around when we turn the sequences sideways?
A full orbit contains all seven nonzero elements, regardless of where it starts. Changing the start only rotates the loop; the generator determines their order. If we pin one element in the first position, the other six can appear in \(6! = 720\) possible orders. Only 48 of those cycles make the three transposed rows shifts of one another. I checked all 720 by brute force; every full field orbit is one of those 48.
Still, an orbit only does one thing: multiply by the same value over and over. Why does that make the rows line up?
Why the Rows Shift
Setup
We want to prove that, in any full orbit, every row is a shift of row 0.
Define \(L=2^n-1\); the number of nonzero elements in the field.
An orbit is:
\[
e(t)=s g^t,
\]
where \(s\) is start and \(g\) is gen, both nonzero.
Because the orbit is full, \(g^0,g^1,\ldots,g^{L-1}\) are distinct and \(g^L=1\).
Multiplying by a fixed nonzero value rearranges the field: every element comes out exactly once, or division would break.
Let \(b_i\) be the function that returns bit \(i\) of an element, counting from the left. For example, \(b_0(110)=1\) and \(b_2(110)=0\).
For each possible shift \(k\), and any element \(z\), define a "shift" function \(S_k\): \[ S_k(z)=b_0(g^kz), \qquad 0\leq k\lt L. \]
Observe: \[ g^k e(t) = g^k s g^t = s g^{k+t} = e(t+k) \]
So then, if we plug in an orbit \(e(t)\) for \(z\), we get: \[ S_k(e(t))=b_0(g^ke(t))=b_0(e(t+k)), \]
In English, this means that \(S_k\) produces row 0 (on the orbit) shifted by \(k\) steps.
We will show that, in a full orbit, for every \(b_i\) there is some \(S_k\) that is the same function.
Counting Readouts
Call any function that takes an element and returns a bit a readout: \[ f:\{n\text{-bit field elements}\}\longrightarrow\{0,1\}. \]
Two readouts are the same when they return the same bit for every element.
A readout is additive if it respects addition: \[ f(u+v)=f(u)+f(v) \]
Both \(+\) signs are the same XOR. Field addition applies it one position at a time, across all \(n\) positions at once. A readout hands back a single bit, so on the right there is only one position left to apply it to.
Addition of any two field elements never mixes the bits across positions. So this means that every \(b_i\) is an additive readout: \[ b_i(u+v) = b_i(u) + b_i(v) \qquad \forall i \]
No \(b_i\) is 0 everywhere, either: \(b_i\) returns 1 on the element whose only 1 sits at position \(i\).
We can count the additive readouts by looking at what they do to elements containing a single 1.
In a three-bit field, suppose \(f\) is additive. Since \(101 = 100 + 001\), \[ f(101)=f(100)+f(001). \]
We can apply the rule repeatedly. Since \(111=110+001\) and \(110=100+010\),
\begin{align*} f(111) &= f(110)+f(001) \\ &= f(100)+f(010)+f(001). \end{align*}
A three-bit field has three single-1 elements: 100, 010, 001.
Every element breaks down into some combination of them, so \(f\) is determined entirely by \(f(100)\), \(f(010)\), and \(f(001)\).
Every other output \(f(u)\) is found by XORing some combination of these three bits.
Each of the three answers can be either 0 or 1, giving at most \(2^3\) additive readouts.
With \(n\) bits, this means there are at most \(2^n\) additive readouts. One is 0 everywhere, leaving at most \(2^n-1=L\) nonzero additive readouts.
The Argument
We just showed that there are at most \(L\) nonzero additive readouts. We also have \(L\) shift functions: \[ S_0,S_1,\ldots,S_{L-1}. \]
If these are all additive, nonzero, and different from one another, they must fill all \(L\) available spots. There are three things to check.
Each \(S_k\) is additive. Multiplication distributes, and \(b_0\) is additive:
\begin{align*} S_k(u+v) &= b_0(g^k(u+v)) \\ &= b_0(g^ku+g^kv) \\ &= b_0(g^ku)+b_0(g^kv) \\ &= S_k(u)+S_k(v). \end{align*}- No \(S_k\) is 0 everywhere. Multiplication by \(g^k\) rearranges the field, so some element \(z\) must satisfy \(g^kz=100\ldots0\). For that element, \[ S_k(z)=b_0(g^kz)=b_0(100\ldots0)=1. \] So \(S_k\) is not 0 everywhere.
No two shift amounts give the same readout. Suppose two different shifts produce the same readout: \(S_j=S_k\), with \(j\neq k\). Then, for every \(z\), \[ b_0(g^jz)=b_0(g^kz). \]
XORing these two equal bits gives 0. Since \(b_0\) is additive,
\begin{align*} 0 &= b_0(g^jz)+b_0(g^kz) \\ &= b_0(g^jz+g^kz) \\ &= b_0((g^j+g^k)z). \end{align*}Because \(j\neq k\), the coefficient \(g^j+g^k\) is nonzero. Multiplication by this coefficient rearranges the field, so the equation would make \(b_0\) return 0 on every element. But \(b_0(100\ldots0)=1\), so that is impossible.
We have found \(L\) distinct nonzero additive readouts, the most there can be. Thus, for every \(i\), there is some \(k\) such that \[ b_i(e(t))=S_k(e(t))=b_0(e(t+k)). \] Every row is therefore a shift of row 0, so every pair of rows differs by a shift. \(\square\)
Why Full Orbits Matter
Suppose the orbit repeats after \(d\) steps, where \(d\lt L\). Then \(g^d=1\), so \[ S_{k+d}(z)=b_0(g^{k+d}z)=b_0(g^kz)=S_k(z). \] There are only \(d\) different shift functions. The count no longer matches: there are \(d\) shift functions, not \(L\), so nothing forces every \(b_i\) to be a shift function.
For example, in the field of four-bit elements (\(2^4=16\) elements), gen=1000 produces this short orbit:
orbit 0001 1000 1100 1010 1111
rows 01111
00101
00011
10001
Row 0 contains four ones, while every other row contains two. A shift preserves that count, so row 0 cannot be a shift of any other row. The property can fail when the orbit is short.
I had tried short orbits earlier, but some of them were deceptive: the active rows were shifts of one another while another row was all zero.
That looked like another version of the same pattern, and I missed that only full orbits guarantee it for every row.
An all-zero row just means that the short orbit never visits an element with a 1 in that bit position.
Conclusion
It turns out I hadn't discovered anything.
In the polynomial view from the optional detour, the connection is literal: our first gen=010 is \(x\), and multiplying by \(x\) shifts the bits left, XORing the modulus back in when a bit falls off the end.
That is a Linear Feedback Shift Register update.
The rows of a full orbit are m-sequences, the sequences produced by maximal-length Linear Feedback Shift Registers. Being shifted copies of one another is one of their standard properties.
So I rebuilt a piece of old hardware by accident, and it happens to make decent drum patterns.
The rhythms work. I'm still looking for the melodies.
AI Disclosure
- AI was used heavily for this post, but nearly all of the prose was rewritten by me.
- Many of the diagrams use AI-generated SVG code, but most were hand-drawn first, then pasted into Claude Code, which generated the SVGs.
- The sound clips were produced by entirely AI-generated code that I did not read.
- The proof went through three major versions, with substantial AI help during development and review.