Everything on this page is new in Harucom OS 2.0.

Johakyu is a live coding environment that drives sound and light from patterns. You perform by writing a DSL, in the manner of TidalCycles and Strudel.

Edit the script and press Ctrl-Enter: the new pattern takes over at the start of the next cycle, without the sound stopping.

It is not only a drum machine — connect the DMX module and it drives lighting as well.

Contents

Starting Johakyu

irb> johakyu
irb> johakyu /data/myshow.rb

Without a file name it opens an untitled buffer. If /data/johakyu/starter.rb exists, it is loaded as a starting point.

Plug headphones or speakers into the 3.5mm jack to hear the sound. For lights, wire up the DMX module first.

Key Bindings

Key Action
Ctrl-Enter Evaluate the buffer (it applies at the next cycle boundary)
Ctrl-S Save and evaluate (you are asked for a path when untitled)
Ctrl-O Open a file
Alt-1 – Alt-0 Switch scenes (ten buffers to move between)
Ctrl-B Blackout
Ctrl-Z / Ctrl-Y Undo / Redo
Ctrl-Q Blackout and quit

The editor behaves like the edit command, with syntax highlighting, auto indent, and Japanese input.

A mistake in your code does not stop the show. When an evaluation raises, it is discarded and the previous patterns keep playing.

Writing Patterns

The repeating unit of time is called a cycle. The tempo is set with tempo.

tempo 120

track(:drums) { sound("bd ~ sd ~") }

This plays kick, rest, snare, rest, spread evenly across one cycle.

Tracks

track(:name) { ... } names a pattern. Tracks are replaced independently, so editing one track leaves the others untouched.

track(:drums) { sound("bd*4") }
track(:hats)  { sound("hh*8") }

_track mutes a track without removing it.

_track(:hats) { sound("hh*8") }

Each evaluation describes the whole state. A track that is no longer in the buffer is removed, so evaluating an empty buffer silences everything.

Mini Notation

Patterns are written as strings, in what is called mini notation.

Syntax Meaning
bd ~ sd ~ A sequence (~ and - are rests)
bd*2 Play twice within the step
bd!3 Repeat across three steps
bd/2 Play once every two cycles
[bd hh] Group into one step
<a b c> One item per cycle
bd, hh*4 Stack in parallel
[c5,e5,g5] Stack inside one step (a chord)
bd:2 Sample number
_ Hold the previous event
track(:drums) { sound("bd*2 [~ sd] bd sd, hh*8") }

Playing Drums

sound takes the name of a drum.

Name Sound
bd Kick
sd Snare
hh Hihat
oh Open hihat
cp Clap
lt Low tom
ht High tom
rim Rimshot

The WAV files in /data/drums are used. When a file is missing, Synth renders the same sound on the board.

Playing Notes

note plays pitched tones.

track(:lead) { note("c5 e5 [c5,e5,g5] ~").sound("saw").gain(0.6) }

c5 is middle C (262 Hz). Sharps are c#5 or cs5 and flats are eb5. Without an octave number, octave 5 is assumed.

.sound picks the waveform (sine, square, tri, saw) and .gain sets the level (0 to 1). Three notes can sound at once.

Transforming Patterns

Patterns are transformed by chaining methods.

Method Effect
fast(n) / slow(n) Speed up / slow down
rev Reverse
every(n) { ... } Transform once every n cycles
euclid(pulses, steps) Euclidean rhythm
degrade_by(amount) Drop events at random
segment(n) Cut a continuous value into n steps
range(min, max) Rescale the values
add / sub / mul / div Arithmetic on the values
track(:drums) { sound("bd*4").every(4) { |p| p.fast(2) } }

Signals

sine, cosine, saw, isaw, tri, and square_signal are values that change continuously over time. They suit levels and light movement.

track(:wash) { dmx(:s1).dimmer(sine.slow(4)) }

range rescales them and slow stretches them out.

Driving Lights

Patching Fixtures

Register the fixtures you use with fixture. The definitions are Open Fixture Library JSON files in /data/dmx/fixtures.

fixture :s1, "shehds_80w_led_spot_light", mode: "13ch", address: 1
fixture :s2, "shehds_80w_led_spot_light", mode: "13ch", address: 14
group :all, :s1, :s2

group collects fixtures so they can be driven together. Put the fixture lines at the top of the script. A script without them keeps the rig that is already patched.

Light Parameters

Chain parameters onto dmx(:name). Values run from 0.0 to 1.0.

track(:s1) { dmx(:s1).dimmer("0 1 1 1").color("<red blue green>") }
track(:move) { dmx(:all).pan(sine.range(0.3, 0.7).slow(8)) }
Parameter Meaning
dimmer Brightness
color Color
pan / tilt Direction
strobe Strobe
gobo Gobo
focus Focus
prism Prism
speed Movement speed

Which parameters work depends on the fixture. Only those in its definition are available.

Names such as colors come from the definition too, so color(:red) works. To write a raw value, use raw.

dmx(:s1).raw(:pan, 200)

Chaining a light control onto a sound pattern makes the light ride the beat.

track(:drums) { sound("bd*4").color("<red blue>") }

spread offsets the values across the members of a group.

track(:chase) { dimmer("1 0").spread(0.5, on: :all) }

Using It from a Program

Patterns can be played from your own program, without the johakyu app.

require "board/pwm_audio"
require "johakyu/dsl"

session = Johakyu::Session.new(audio: Board::PWMAudio.new, bpm: 120)
session.load_kit
session.bind_statement(:drums, Johakyu.sound("bd*4 , hh*8"))

loop do
  session.update
  sleep_ms 10
end

Call session.update on every iteration, without fail. That is where sounds are reserved and light values are written. When lights are involved, call DMX.keepalive as well.