Timpani's Website

RhythmScript-DDR Specification


Introduction

RhythmScript is a minimal scripting language for creating arbitrary rhythms.

Please refer to the example at the bottom of the page.

Design Philosophy

Writing a rhythm should be as easy as typing on a keyboard. Metric modulation is a common enough concept in experimental music that it should be built-in and easy to use. It's just tempo ratios and regrouping, which are very easy to implement.


Tempo, Subdivision, and Superdivision

The tempo is set with the -tempo keyword, followed by a number (with or without a decimal point). The tempo is in beats per minute.

The -subdiv keyword sets the subdivision of the beat. For example, -subdiv2 means that the beat is divided into 2 equal parts, -subdiv3 means that the beat is divided into 3 equal parts, and so on.

The -superdiv keyword does the opposite: it sets the new beat to take the same amount of time as n of the old beats.

Subdivision and superdivision applied directly to the CURRENT internal pulse. It is NOT relative to the most recent -tempo command.


Countdowns

The -321go and -readysetgo keywords are DDR-inspired game exclusive. They are visual cues for the player to start playing the rhythm.

In the JavaScript implementation, these keywords just send messages to the canvas. In the Unity implementation, they will trigger a countdown animation and any other necessary visual effects.


Literals

Any non-whitespace, non-keyword character is interpreted as a literal. Each character in a literal plays for the duration of the current tempo.

Remember that whitespace is ignored, so udlr udlr is the same as udlrudlr. This is just used for organizing the script into readable chunks.

What the Characters Mean (in our DDR-inspired game)

Ultimately, the meaning of the characters is up to the game or program that is interpreting the RhythmScript. In our DDR-inspired game, the characters correspond to the arrow keys on the keyboard.

Standard Character Meaning
u Up
d Down
l Left
r Right
x Rest/No input

Additionally, there are characters that represent directions NOT to press. Imagine them as the bombs in Fruit Ninja.

Anti-Character Meaning
U No Up
D No Down
L No Left
R No Right

NOTE: As of 2024-02-15, anti-characters are not implemented yet.


Simultaneous Events

The + operator indicates that the surrounding literal characters should be played at the same time. For example, udlr+udlr means to play udl in sequence, r+u simultaneously, and then dlr in sequence.

Under the hood, the + operator ticks the clock backwards by 1 beat at the current tempo. This technically means you can have several + operators in a row to travel back in time by several beats. This is recommended for polyrhythms and other complexly-layered rhythms.


Blocks and Repetition

Blocks are defined with curly braces {}. The contents of the block are repeated a certain number of times, which is specified by a number after the block. For example, {udlr}.4 means to play udlr 4 times.

You can also nest blocks within other blocks.

Because whitespace is ignored, blocks can be written on multiple lines and indented for readability.


Freestyle Sections

For the specific implementation of RhythmScript within our DDR-inspired game, we have a -free keyword that starts a freestyle section. This means that the player can hit any button at any time, and the game will not penalize them. The -unfree keyword ends the freestyle section.

In a more general implementation, there would be no need for these keywords.


Printing Debug Messages

The -print keyword is followed by a string, which is printed to the console. This is useful for debugging. Leading and trailing whitespace is ignored, but spaces within the string are preserved.


Comments and Organization

Because there is no built-in synchronization with the music, you should organize your RhythmScript to ensure you know where in the piece of music you are.

To do this, the parser ignores the following things:

  • Anything after // on a line
    • Use these to explain what a line does
  • Anything between /* and */
    • Use these for longer descriptions if you wish
    • or to have a comment in the middle of a line
      • (not recommended in most cases)
  • Any spaces, tabs, or newlines
    • Except for spaces in -print statements
    • Use these to organize literals into readable chunks (can be read as "measures" or "beats")

Example

-tmpo128 // tempo = 128
-subdiv2 // get 8th notes
-print Starting intro

-321go

udlrudlr udlrxxxx

u+r
u+r
u+r
u+r         // 8 combos of 2 arrows at once
d+l
d+l
d+l
d+l

{
    // literal
    ududlrlr
    
    xxxxx // rest

    // nested loop: do this 8 times
    {
        uxxuxxux // tresillo
    }.8
}.2
// (do the above block within curly braces 2 times)

// go twice as fast
-subdiv2

lrlrlrlrlrlrlrlr
udududududududud

-free
// start freestyle section (not necessary to hit these beats)


-print Intro done; starting A section

// reset the tempo to any value @ any time
-tempo90

// metric modulation: q's -> 8ths -> dotted q's
-subdiv2
-superdiv3

{
    udlrudl    // group length/"measure" length is arbitrary
    rludrlu    // here's 7 dotted q's grouped
}.8

-unfree      // no longer freestyle section

{lrrlrrlr}.8 // do this 8 times