Back Original

Clapping Music for one flip-disc display: Byte and variations

In response to previous post Clapping Music for two flip-disc displays, a reader commented

I’d love to see a version played on a single board, with the two performers represented by the left and right sides of the board. It would more closely match the layout of a typical performance (two people standing side by side), and I think it would make it easier to see the phasing points.

Great idea—let’s do it!

Quite pleasing. Thanks for the suggestion!

***

The code changes to support this variation were fairly minimal, but in the process of implementing them I accidentally stumbled into a nice illustration of how commands are sent to the flip-disc board. Let’s take a look!

Clapping Anything Music

First, the minimal code changes to support the new playback mode. If you’ll recall from the last post, our code that looked like this:

function clapping_music(sink_dots, sink_digits; pause=0.15,
                        clap_pattern=Bool[1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0],
                        num_repeats=12, num_shifts=length(clap_pattern) + 1,
                        num_dots_to_set=28, num_digits_to_set=2)
    i_pattern_shift = 0
    for _ in 1:num_shifts
        for _ in 1:num_repeats, i_pattern in eachindex(clap_pattern)
            clap_pattern[i_pattern] &&
                write_to_sink(sink_dots, rand(0x00:0x7F, num_dots_to_set))
            clap_pattern[mod1(i_pattern + i_pattern_shift, length(clap_pattern))] &&
                write_to_sink(sink_digits, rand(0x00:0x7F, num_digits_to_set))
            sleep(pause)
        end
        i_pattern_shift += 1
    end
end

Pulling the “make a clap” phrases out into input arguments clap_a and clap_b fully decouples the performance from the flip-disc boards:

function clapping_music(; clap_a=() -> print("A"), clap_b=() -> print("B"),
                        pause=0.15, clap_pattern=Bool[1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0],
                        num_repeats=12, num_shifts=length(clap_pattern))
    i_pattern_shift = 0
    for _ in 0:num_shifts
        for _ in 1:num_repeats, i_pattern in eachindex(clap_pattern)
            clap_pattern[i_pattern] && clap_a()
            clap_pattern[mod1(i_pattern + i_pattern_shift, length(clap_pattern))] && clap_b()
            sleep(pause)
        end
        i_pattern_shift += 1
    end
end

Now it can be used to perform Clapping Music with any two clap functions that you want: lights, sounds, motors, whatever you want.1 2

The default behavior is to print a comment to the command line, which gives a lovely realtime-captioned mashup of Clapping Music with John Cage’s 4’33”:

Animation of the letters “A” and “B” being typed in sequence

A transcription of the full performance is even less interesting:

julia> clapping_music()
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAA
BABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAA
BBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBAABABABABABABABBAABABABABABABABB
AABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABA
BABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABA
BABABABBABABABAABBABABABABABABAABBABABABABABABAABBABABABABABABAABBABABABABAB
ABAABBABABABABABABAABBABABABABABABAABBABABABABABABAABBABABABABABABAABBABABAB
ABABABAABBABABABABABABAABBABABABABABABAABBABABABABAABABABABBABABABAABABABABB
ABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABA
BABBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAA
BABABABBABABAABABABABABBAABBAABABABABABBAABBAABABABABABBAABBAABABABABABBAABB
AABABABABABBAABBAABABABABABBAABBAABABABABABBAABBAABABABABABBAABBAABABABABABB
AABBAABABABABABBAABBAABABABABABBAABBAABABABABABBAABBABAABBAABBABABABABAABBAA
BBABABABABAABBAABBABABABABAABBAABBABABABABAABBAABBABABABABAABBAABBABABABABAA
BBAABBABABABABAABBAABBABABABABAABBAABBABABABABAABBAABBABABABABAABBAABBABABAB
ABAABBAABBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABABABBAB
ABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABAABABABA
BBABABABAABABABABBABABABAABABABABBABABABAABABABABBABABABABABABABABABBAABABAB
ABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAAB
ABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABABBAABABABABABABAB
BAABABABABABABABBAABABAABBABABABABABABAABBABABABABABABAABBABABABABABABAABBAB
ABABABABABAABBABABABABABABAABBABABABABABABAABBABABABABABABAABBABABABABABABAA
BBABABABABABABAABBABABABABABABAABBABABABABABABAABBABABABABABAABABBAABBABAABB
AABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBAB
AABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAABBABAABBAABABBAA
BBABAABBAABABBAABBABAABBABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB

Anyway.

To recreate the dual-board performance of the original post, we do

clap_a = () -> write_to_sink(sink_dots, rand(0x00:0x7F, 28))
clap_b = () -> write_to_sink(sink_digits, rand(0x00:0x7F, 2))

# Play it:
clapping_music(; clap_a, clap_b)

and the new single-board performance is generated with

function randomize_cols!(board_state, col_range) 
    board_state[col_range] .= rand(0x00:0x7F, length(col_range))
    return board_state
end

board_state = zeros(UInt8, 28)
clap_left!() = write_to_sink(sink_dots, randomize_cols!(board_state, 1:10))
clap_right!() = write_to_sink(sink_dots, randomize_cols!(board_state, 18:28))

# Play it:
clapping_music(; clap_a=clap_left!, clap_b=clap_right!)

You’ll note that this single-board implementation involves keeping track of—and updating—the full display state of the board throughout the duration of the piece (via board_state). This is necessary because a single update of the display board updates all discs on the board, so we can’t update just a few columns with each clap.3 Instead, we send the complete desired state to the board each time we want the board to update.

Binary diversion

I didn’t remember exactly how the “send update to boards” function worked—it had been awhile since I wrote it!—and my first guess, before going back to read the code, ended up not being what I intended BUT looking delightful anyway:4

As those of you familiar with counting in binary have likely recognized, in this variation, for each the right and left clappers, we are counting the number of claps that have been clapped thus far.

In these displays, one byte of data corresponds to one column of flip-disc board. Let’s send bytes values 0 through 127 (i.e., 27) one at a time, to illustrate:

for x in 0x00:0x7F
    write_to_sink(sink_dots, [x])
    sleep(0.1)
end

For the fun of it, let’s do all columns at the same time:

for x in 0x00:0x7F
    write_to_sink(sink_dots, fill(x, 28))
    sleep(0.2)
end

Given that knowledge, here’s the most basic “flip only one disc at a time” Clapping Music—aka, “The Littlest Clap”, as requested by AF:

board_state = zeros(UInt8, 28)

function clap_a!()
    board_state[14] = board_state[14] == 0 ? 1 : 0
    return write_to_sink(sink_dots, board_state)
end

function clap_b!()
    board_state[15] = board_state[15] == 0 ? 1 : 0
    return write_to_sink(sink_dots, board_state)
end

clapping_music(; clap_a=clap_a!, clap_b=clap_b!, num_repeats=2, num_shifts=1)

Playtime

Here are another few examples of flip-disc behavior, because I think they’re fun.

Single column refresh rate

Flipping a whole column on and off, with a decreasing amount of time between “off” and “on”:

for s in 0.1:-0.001:0.0
    clear(sink_dots)
    sleep(s)
    write_to_sink(sink_dots, fill(0x7F, 1))
    sleep(.1)
end

Shimmer

Flipping a whole array off and on, with no pause in between—i.e., the board updates are sent faster than they can be (mechanically) fulfilled:

for _ in 1:30
    clear(sink_dots)
    write_to_sink(sink_dots, fill(0x7F, 10))
end

Multicolumn refresh rate

Flipping a set of counter columns on and off, again without enough time between requests for the board to fully update:

for x in 0x00:0x7F
    clear(sink_dots)
    sleep(0.2)
    write_to_sink(sink_dots, fill(x, 10))
end

***

That’s all for now!

Code to generate all these examples lives here. Have an idea for a cool demo but don’t have access to flip-disc boards? Send me a snippet of code and I’ll run it for you!