In the beatmap file, before specifying the notes in a row, some control sentences can be included for the row. Each control sentence includes a keyword and one or more parameters, separated by spaces. Keywords are case-sensitive and are identifiers of various control sentences.

The following are specifications of the control sentences. Sometimes these control sentences make use of an expression or an expression without x, see Expressions.

PERFECT, GOOD, BAD

Syntax:

PERFECT <windowRadius>
GOOD <windowRadius>
BAD <windowRadius>

These control sentences accept one parameter, indicating the radius of the judgement window. The windowRadius is NOT in milliseconds but is the ratio of the inaccuracy tolerance for a perfect / good / bad judge and the total (temporal) length of the row. Therefore, given the same radius of judgement window, judging will be stricter if the row is shorter (in time).

BPM

(Also alised as BEATS_PER_MINUTE.)

Syntax:

BPM <beatNote1> <beatsPerMinute1>[ <position1>[ <beatNote2> <beatsPerMinute2> <position2>[ ... ]]]

The beatNote is a note specified in the same manner here. The beatsPerMinute is the number of beats per minute, meaning that one minute is exactly beatsPerMinute times as long as the beat note. The position is the position of the BPM change, specified as a rational number in $[0, 1]$, with the start of the row being 0 and the end of the row being 1. The position is calculated according to notes’ literal length but not their temporal length (e.g. a quavar in 180 BPM and one in 200 BPM has the same literal length because they are both quavars).

MS_PER_WHOLE

(Also alised as MILLISECONDS_PER_WHOLE.)

Syntax:

MS_PER_WHOLE <millisecondsPerWhole>

This sets the average temporal length of a whole note in milliseconds. Usually this can be automatically calculated by the game according to the BPM specified by BPM, or inherit from last row. However, sometimes it is necessary to specify it manually, especially when using TIME.

TIME

Syntax:

TIME <expression>

Here <expression> is a single-variable mathematical expression of variable x. See Expressions.

The expression is a mapping from $[0, 1]$ to $[0, 1]$. It must be monotonically increasing (because you cannot travel to the past). It maps the position in the row to the time at which the position in the row is played. The position mapped to 0 is played exactly when the row starts being played, and the position mapped to 1 is played exactly when the row ends being played (and when the next row starts being played). For example, with the expression sqrt(x) you can have the tempo linearly (w.r.t. notes’ literal lengths) increasing from 0.

Default: x.

JUDGEMENT_LINE_X, NOTE_X, HIT_X, BAR_LINE_X

Syntax:

JUDGEMENT_LINE_X <expression>
NOTE_X <expression>
HIT_X <expression>
BAR_LINE_X <expression>

Here <expression> is a single-variable mathematical expression of variable x. See Expressions.

The expression is a mapping from $[0, 1]$ to $[0, 1]$. The JUDGEMENT_LINE_X sentence maps the position of the row (according to note lengths) to the spatial position. The position mapped to 0 is drawn at the leftmost place, and the position mapped to 1 is drawn at the rightmost place. It is not necessarily monotonically increasing (to possibly make the judgement line move to the left) or being continuous (to possibly make the judgement line jump suddenly).

NOTE_X and HIT_X do similar things to what JUDGEMENT_LINE_X does, but NOTE_X defines the spatial position of drawn notes while HIT_X defines the spatial position of the hit effects.

The default setting of JUDGEMENT_LINE_X is x. The default setting of NOTE_X is to be the same as JUDGEMENT_LINE_X. The default setting of HIT_X is to be the same as NOTE_X. The default setting of BAR_LINE_X is to be the same as NOTE_X.

JUDGEMENT_LINE_Y, JUDGEMENT_LINE_WIDTH, JUDGEMENT_LINE_HEIGHT

Syntax:

JUDGEMENT_LINE_Y <expression>
JUDGEMENT_LINE_WIDTH <expression>
JUDGEMENT_LINE_HEIGHT <expression>

Here <expression> is a single-variable mathematical expression of variable x. See Expressions.

These control sentences are purely for ornamental performance of the judgement line because they do not affect the (spatial and temporal) arrangement of notes. The expressions are mappings on $[0, 1]$, and the mapped values are lengths in unit of pixels.

JUDGEMENT_LINE_Y is the vertical position of the judgement line. Positive values mean to place the judgement line at specified number of pixels above the default position. Default: 0.

JUDGEMENT_LINE_WIDTH is the width of the judgement line. Default: 1.

JUDGEMENT_LINE_HEIGHT is the height of the judgement line. Default: voicesHeight times the number of voices.

These control sentences are ineffective if the user disabled ornamental judgement line performances in the preferences.

JUDGEMENT_LINE_RED, JUDGEMENT_LINE_GREEN, JUDGEMENT_LINE_BLUE, JUDGEMENT_LINE_ALPHA

(JUDGEMENT_LINE_ALPHA is also alised as JUDGEMENT_LINE_OPACITY.)

Syntax:

JUDGEMENT_LINE_RED <expression>
JUDGEMENT_LINE_GREEN <expression>
JUDGEMENT_LINE_BLUE <expression>
JUDGEMENT_LINE_ALPHA <expression>

Here <expression> is a single-variable mathematical expression of variable x. See Expressions.

These control sentences are also purely for ornamental performance of the judgement line. They are used to change the color of the judgement line. These expressions are mappings from $[0, 1]$ to $[0, 1]$, and the default of them are all 1 (pure and non-transparent white).

These control sentences are ineffective if the user disabled ornamental judgement line performances in the preferences.

JUDGEMENT_LINE_ROTATION

JUDGEMENT_LINE_ANCHOR_X, JUDGEMENT_LINE_ANCHOR_Y

JUDGEMENT_LINE_Z

JUDGEMENT_LINE_BLEND_MODE

Syntax:

JUDGEMENT_LINE_BLEND_MODE <blendModeName>

This control sentence changes the blend mode of the judgement line. Available options for blendModeName are (alphabetically, case-insensitive)

  • ADD,
  • ADD_NPM,
  • COLOR,
  • COLOR_BURN,
  • COLOR_DODGE,
  • DARKEN,
  • DIFFERENCE,
  • EXCLUSION,
  • HARD_LIGHT,
  • HUE,
  • LIGHTEN,
  • LUMINOSITY,
  • MULTIPLY,
  • NORMAL,
  • NORMAL_NPM,
  • OVERLAY,
  • SATURATION,
  • SCREEN,
  • SCREEN_NPM,
  • SOFT_LIGHT.

If the player enables the WebGL render in his preferences, only NORMAL, ADD, MULTIPLY, and SCREEN are supported, and the other blend modes silently act like NORMAL.

(It seems that there lacks documents about how these blend modes work. You can look at this document as a reference or just play around by yourself.)

Default: NORMAL.

FAKE_JUDGEMENT_LINE

Syntax:

FAKE_JUDGEMENT_LINE [<label>]

This control sentence creates a new judgement line for this row of beatmap but a fake one (which does not affect the gameplay but is purely ornamental). After this control sentence, you can add the following control sentences to define the behaviors of the new fake judgement line without affecting the judgement lines defined previously:

You can create more fake judgement lines by using FAKE_JUDGEMENT_LINE again after finishing defining the previous fake judgement line. Later created fake judgement lines appear on top of previously created fake judgement lines. Fake judgement lines appear on top of the real judgement line.

You can also specify the label parameter to give the fake judgement line a label for it to be referred to later. Here, label is specified as an expression without x. If the a judgement line with the specified label does not exist in the current row, a new fake judgement line will be created for the row. If a judgement line with the specified label is already created in the current row, you can now use those JUDGEMENT_LINE_* control sentences to modify the judgement line with the label.

This control sentence is not effective if the user disables ornamental judgement line performances in the preferences.

GENUINE_JUDGEMENT_LINE

Syntax:

GENUINE_JUDGEMENT_LINE

TEXT

TEXT_TEXT

TEXT_X, TEXT_Y, TEXT_Z, TEXT_ANCHOR_X, TEXT_ANCHOR_Y, TEXT_ROTATION, TEXT_RED, TEXT_GREEN, TEXT_BLUE, TEXT_ALPHA, TEXT_BLEND_MODE

TEXT_SCALE_X, TEXT_SCALE_Y

LET

Syntax:

LET <identifier> <expression>

LET can define a variable in the scope with x. After a variable is defined by LET, it can be referred to in any expression with x, but it cannot be referred to in any expression without x. The identifier must consist only of digits, letters, or underscores, and not start with a digit (in regexp: [a-zA-Z_]\w*). The rule also applies to the identifier specified to variables defined by DEF, VAR, and FUN.

Example 1:

LET y x^2
TIME (x+y)/2

In this example, a variable with x y is defined using LET. It is then referred to in the TIME control sentence. This is equivalent to

TIME (x+x^2)/2

Example 2:

LET y x^2
TIME (x+$y)/2
JUDGEMENT_LINE_X (x+y)/2
LET y x^3

In this example, the variable y is value-lockedly referred to in the TIME control sentence (because it is referred to as $y with the dollar symbol). If the reference of a variable is value-locked, it will not be affected if the variable is overridden in the future. However, the variable y is normally referred to in the JUDGEMENT_LINE_X control sentence. The codes are equivalent to

TIME (x+x^2)/2
JUDGEMENT_LINE_X (x+x^3)/2

DEF

(Also aliased as DEFINE.)

Syntax:

DEF <identifier> <parameters> <expression>

DEF defines a function in the scope with x. parameters is a list of parameter names seperated by commas (, without whitespace). In expression, the parameters can be referred to as independent variables, as well as x.

A function defined by DEF can be invoked in any expression with x. When a function defined by DEF is invoked, parentheses should be used to specify the value of parameters. Functions defined by DEF cannot be referred to in expressions without x.

The parameters list cannot be empty. Actually, if the parameters list is empty, it should be defined using LET instead of DEF.

Example 1:

DEF f m x^m
TIME (x+f(2))/2

In this example, the function f is defined using DEF, and it has one parameter called m. Then, the function is referred to and invoked in the TIME control sentence, with the parameter m having value 2. The codes are equivalent to

TIME (x+x^2)/2

Example 2:

DEF f m (x + x^m) / 2
TIME $f(2)
JUDGEMENT_LINE_X f(3)
DEF f m log(1 + m x) / log(1+m)

In this example, the function f is value-lockedly referred to in the TIME control sentence, while it is referred to normally in the JUDGEMENT_LINE_X control sentence. The codes are equivalent to

TIME (x + x^2) / 2
JUDGEMENT_LINE_X log(1 + 3 x) / log(4)

VAR

(Also aliased as VARIABLE.)

Syntax:

VAR <identifier> <expressionWithoutX>

VAR sentences define variables in the scope without x. Such variables can be referred to in both expressions with x and expressions without x.

Example 1:

VAR a 2003
VAR b a - 895
VAR a 520

# outputs 520:
DEBUG_LOG a

# outputs 1108:
DEBUG_LOG b

Example 2:

VAR m 2
TIME (x+x^m)/2
JUDGEMENT_LINE_X (x+x^$m)/2
VAR m 4

This example illustrates that variables defined by VAR can be referred to in a value-locked way. It is equivalent to

TIME (x+x^4)/2
JUDGEMENT_LINE_X (x+x^2)/2

FUN

(Also aliased as FUNCTION.)

Syntax:

FUN <identifier> <parameters> <expressionWithoutX>

FUN defines functions in the scope without x. parameters is a list of parameters separated by commas (, without whitespace).

The parameters list cannot be empty. However, functions with no parameters cannot be defined by VAR (this case is different from how DEF and LET can replace each other). Technically, you cannot define a function with no parameters in the scope without x, but a workaround is to just ignore what you have put in the parameters list (see example 2).

Example 1:

FUN pythagoras a,b sqrt(a^2+b^2)

# outputs 5:
DEBUG_LOG pythagoras(3,4)

Example 2:

VAR m 1
FUN f _ $m
FUN g _ m
VAR m 2

# outputs 1:
DEBUG_LOG f()

# outputs 2:
DEBUG_LOG g()

PROCEDURE

Syntax:

PROCEDURE <keyword>
  <block>
END

PROCEDURE enable you to define your own control sentences (without parameters). keyword specifies the keyword of the new control sentence, which is case-sensitive and must consist of digits, letters, and underscores and start with capitalized letters or an underscore (regexp: [A-Z_]\w*). block contains a sequence of control sentences which will be executed when the newly-defined control sentence is invoked.

If a control sentence with keyword being the same as the keyword here, the old one will be overridden by the newly-defined one. The aliases of the old one will not be affected (see ALIAS).

Example:

# This control sentence can calculate the factorial of variable `input` and print it
PROCEDURE PrintFactorialOfInput
  VAR result 1
  VAR i 1
  WHILE i <= input
    VAR result result * i
    VAR i i + 1
  END
  DEBUG_LOG result
END

VAR input 5

# outputs 120:
PrintFactorialOfInput

UNPROCEDURE

Syntax:

UNPROCEDURE <keyword> [<keyword2> [...]]

UNPROCEDURE can undefine control sentences specified by keyword etc. If the newly-undefined control sentence has any aliases, the aliases will not be affected (see ALIAS).

ALIAS

Syntax:

ALIAS <newKeyword> [<newKeyword2> [...]] <oldKeyword>

ALIAS defines new control sentences which have the same effect as the control sentence specified by oldKeyword, but with different keyword specified by newKeyword etc.

Example 1:

ALIAS DL DEBUG_LOG
UNPROCEDURE DEBUG_LOG

# outputs "Hello, world!" (without quotes):
DL 'Hello, world!'

Example 2:

PROCEDURE SomeProcedure
  DEBUG_LOG 'old'
END
ALIAS OldSomeProcedure SomeProcedure
PROCEDURE SomeProcedure
  OldSomeProcedure
  DEBUG_LOG 'new'
END

# outputs "old", and then outputs "new" (without quotes):
SomeProcedure

IF

Syntax:

IF <expressionWithoutX>
  <block>
[ELSE_IF <expressionWithoutX2>
  <block2>]
[...]
[ELSE
  <blockN>]
END

IF helps you build up control flow of control sentences.

WHILE

Syntax:

WHILE <expressionWithoutX>
  <block>
END

WHILE helps you build up control flow of control sentences. Control sentences in block will be executed cyclically while expressionWithoutX evaluates true.

In block, BREAK can be used to jump out of the cycle.

Example:

# This control sentence can calculate the factorial of variable `input` and print it
PROCEDURE PrintFactorialOfInput
  VAR result 1
  VAR i 1
  WHILE i <= input
    VAR result result * i
    VAR i i + 1
  END
  DEBUG_LOG result
END

VAR input 5

# outputs 120:
PrintFactorialOfInput

FOR

Syntax:

FOR <variable>[,<indexVariable>] <expressionWithoutX>
  <block>
END

BREAK

Syntax:

BREAK[ <layer>]

BREAK makes the execution of control sentences jump out of cycles. It can only be used inside blocks of cycles like WHILE.

layer is a non-negative integer. It specifies how many layers out of which this BREAK control sentence jumps. If layer is not specified, the default value is 0, which means jump out of one layer of cycle.

Example 1:

# outputs 1, 2, 3, 4, 5 one by one:
VAR m 0
WHILE true
  IF m > 5
    BREAK
  END
  DEBUG_LOG m
  VAR m m + 1
END

Example 2:

VAR n 0
WHILE true
  WHILE true
    IF n > 5
      # breaks the outer cycle
      BREAK 1
    END
    VAR n n + 1
  END
END

# outputs 6
DEBUG_LOG n

DEBUG_LOG

Syntax:

DEBUG_LOG <expressionWithoutX>

DEBUG_LOG is used to output something in the console. It does not affect anything else.

COMMENT

Syntax:

COMMENT <anything>

This control sentence is of no use.