prelude
The prelude has no namespace prefix. Everything here is available as a bare name.
Control
Section titled “Control”wait(secs: num) -> void ✅stop(stop_option: StopType = StopType.ALL) -> void ✅wait(0.5);stop(StopType.THIS_SCRIPT);StopType
Section titled “StopType”| Member | Value |
|---|---|
StopType.ALL |
"all" |
StopType.THIS_SCRIPT |
"this script" |
StopType.OTHER_SCRIPTS_IN_SPRITE |
"other scripts in sprite" |
Constants
Section titled “Constants”public true: boolpublic false: boolScratch has no boolean literal, so these are declared rather than lexed: true is
"true" == "true" and false is "false" == "".
Used by events.onKey and sensing.keyPressed.
| Member | Value |
|---|---|
Key.SPACE |
"space" |
Key.LEFT_ARROW |
"left arrow" |
Key.RIGHT_ARROW |
"right arrow" |
Key.UP_ARROW |
"up arrow" |
Key.DOWN_ARROW |
"down arrow" |
Key.ANY |
"any" |
Key.NUM_0 … Key.NUM_9 |
"0" … "9" |
Key.a … Key.z |
"a" … "z" |
The letter members have no explicit value. Because Key is a stdlib enum, they fold to the
bare member name — Key.a is "a", which is exactly what the Scratch field wants — so
both forms work everywhere:
events.onKey(Key.a) { ... }events.onKey("a") { ... } # identicalsensing.keyPressed(Key.UP_ARROW);sensing.keyPressed("up arrow"); # identicalStrings and variables
Section titled “Strings and variables”len(input: str) -> num ✅ # operator_lengthshowVariable(variable: any) -> void ✅hideVariable(variable: any) -> void ✅showVariable and hideVariable need a plain variable reference, not an expression —
the Scratch block takes a variable field, not an input.
showVariable(score); # ✅showVariable(score + 1); # ⛔Operator procedures
Section titled “Operator procedures”These exist so <=, >=, ^, !&, !| and !^ have something to compile to. You never
call them by name; the IR routes the operator through them and inlines the body.
nand(a: bool, b: bool) -> bool # !&nor (a: bool, b: bool) -> bool # !|xnor(a: bool, b: bool) -> bool # !^xor (a: bool, b: bool) -> bool # ^lte (a: any, b: any) -> bool # <=gte (a: any, b: any) -> bool # >=See Operators.
Iteration helpers
Section titled “Iteration helpers”range(stop: num) -> list<num> ✅ in a for headerrange(start: num, stop: num, step: num = 1) -> list<num> ✅ in a for headerfor (n, range(2, 10, 2)) { ... }range in a for header is folded into the loop counter and never built as a list. That
is the only place it works — range() used as a value has no codegen:
temp xs: list<num> = range(5); # ⛔ no slot metadatazip(l1: list<T>, l2: list<U>) -> (list<T>, list<U>) ⛔enumerate(items: list<T>) -> (list<num>, list<T>) ⛔Both infer their return types correctly and then fail codegen. Walk by index instead:
for (i, names.length()) { report(names[i], powers[i]);}Num(value: any) -> num ⛔Str(value: any) -> str ⛔Bool(value: any) -> bool ⛔List(value: any) -> list<any> ⛔typeof(value: any) -> str ⛔All five resolve to katnip_* opcodes with no codegen metadata. Using one throws
no slot metadata at build time.
For num → str, an interpolated string works today and lowers to join:
temp s: str = f"{count}";Scratch coerces at runtime anyway, so most casts are only about satisfying the type
checker. Where you need to opt out, annotate any.