Skip to content

benchmarko/LocoBasic

Repository files navigation

LocoBasic

LocoBasic is a lightweight adaptation of Locomotive BASIC designed mainly for calculations and simple animations. Programs are compiled to JavaScript and can run in a web browser or Node.js.

Main features:

  • Simple BASIC dialect inspired by Locomotive BASIC
  • Runs in browser or Node.js
  • Compiles BASIC to JavaScript
  • Designed for numerical calculations with JavaScript performance
  • Can also create and animate Scalable Vector Graphics (SVG)
  • GOTO is intentionally not supported

Quick Example

PRINT "Hello World"

Try it here: LocoBasic

  • Select an example from the dropdown menu or input your own BASIC code. The code is automatically compiled to JavaScript and executed unless you disable the Auto compile or Auto execute options.

  • When you change the UI (e.g., select an example or show/hide text editors), the URL is updated to reflect the new state. This allows you to reload the page to start the app with the modified state.

Running

Example (Node.js):

node dist/locobasic.js input='PRINT "Hello"'

Other examples:

node dist/locobasic.js example=euler
node dist/locobasic.js input='PRINT "Hello!"'
node dist/locobasic.js input="?3 + 5 * (2 - 8)"
node dist/locobasic.js example=binary database=rosetta databaseDirs=examples,https://benchmarko.github.io/CPCBasicApps/rosetta
node dist/locobasic.js grammar=strict ... (strict mode: see below)

This command compiles the input code into a standalone JavaScript file which can output text:

npx ts-node dist/locobasic.js input='PRINT "Hello!"' action='compile' > hello1.js
node hello1.js

Keyword reference

LocoBasic is a lightweight adaptation of Locomotive BASIC, not all keywords are available.

Not-supported commands, functions and features known from Locomotive BASIC (usually) produce a warning.

Keywords must be written in either all uppercase (preferred) or all lowercase characters.

Commands

AFTER

  • AFTER timeout [, timer] GOSUB line Calls subroutine line after timeout*20 ms (1/50 seconds).
  • An optional timer can be 0..3. (Priorities are not implemented in LocoBasic.)
  • See also: GOSUB, EVERY, REMAIN.

CLEAR INPUT

  • CLEAR INPUT Clear the key input buffer for INKEY$.
  • See also: INKEY$.

CLS

DATA

  • DATA ["string", number,...] Stores literal data values (string or numerical) for READ.
    • Separated by commas ",".
    • Strings must be quoted.
    • Numbers (including hex and binary) are unquoted and can only be read numerically.
  • See also: READ, RESTORE.

DEF FN

  • DEF FNname[(arg1, ...)] = expression Defines a user defiend function FNname.
  • The defined function can be called as FNname() or FN name().
  • If there are no arguments, do not use parentheses.
  • See also: FN.

DEFINT

  • DEFINT a Defines, that (numerical) array variables starting with letter "a" will be integer. - That means, when you use DIM a(num), a typed Int16Array is created instead of an untyped Array.
  • Note: In LocoBasic, activation of the functionality is determined at compile time starting from its lexical position and not dynamically during execution. Has has only an effect for following DIM with one dimension, not on plain variables. Do not rely on that the integer will be 16 bit. The implementation may use Int32Array or something else later.

DEG

  • DEG Switches to degrees mode for the functions ATN, COS, SIN and TAN.
  • Note: In LocoBasic, the flag is used at compile time starting from its lexical position and not dynamically during execution.
  • See also: RAD.

DIM

  • DIM arrayVariable(dim1 [, dim2, ...]) Declares and initializes an array.
  • Arrays are zero-based.
  • Upper bounds are inclusive.
  • Arrays can be multi-dimensional (unlimited dimensions in LocoBasic).
  • Elements will be initialized with 0 or "" depending on the variable type.
  • Note: In LocoBasic, array variables cannot have the same name as normal variables. So it is not possible to have variable "a" and "a()" at the same time.
  • See also: ERASE.

DRAW

DRAWR

ELSE

  • IF expression THEN... ELSE Optional alternative part of IF.
  • See also: IF.

END

  • END Ends execution.
    • This is currently the same as STOP.

ENDIF

  • ENDIF Ends a multi-line IF. (Only supported in LocoBasic.)

ERASE

  • ERASE variable, [variable,...] Erases array variables.
    • Specify variable name without indices.

ERROR

  • ERROR number Throws an error with number.

EVERY

  • EVERY timeout [, timer] GOSUB line Calls subroutine line in intervals of timeout*20 ms (1/50 seconds).
  • An optional timer can be 0..3. (Priorities are not implemented in LocoBasic.)
  • See also: AFTER, GOSUB, REMAIN.

FOR

  • FOR variable = start TO end [STEP increment] Control structure.
  • increment can also be negative, in which case start must be greater than end.
  • Ended with NEXT.

FRAME

  • FRAME Pauses execution for (up to) ~50ms intervals for synchronization.
  • This command will also flush text and graphical output.

Example to synchronizes execution:

t = TIME + 50: WHILE TIME < t: FAME: WEND

GOSUB

  • GOSUB line Calls subroutine starting at line.
  • Subroutines must end with a single RETURN on its own line.

GRAPHICS PEN

  • GRAPHICS PEN number Sets the graphics pen (0..15)

IF

  • IF expression THEN statements [ELSE statements] control structure (in one line).

INK

  • INK pen,color Sets the color (0..27) for PEN pen and GRAPHICS PEN pen (0..15).
  • Note: In LocoBasic, only following drawings get the new color, existing drawings are not modified.
  • See also: PEN, GRAPHICS PEN.

INPUT

  • INPUT [message;] variable [, variable, ...] Prompts the user for input (string or numeric).
  • The input is split at "," and the parts are assigned to multiple variables
  • Note: Currently the variables must have the same type.
  • See also: LINE INPUT.

KEY DEF

  • KEY DEF 78,1,k Creates a user-defined input button for a key with ASCII code k on the UI.
  • This is only available in LocoBasic and should have "no function" in Locomotive BASIC.
  • Other variants of KEY DEF (known from Locomotive BASIC) are not supported.
  • See example testkey.

LINE INPUT

  • LINE INPUT [message;] variable Prompts the user for a line of input (string).
  • See also: INPUT.

MODE

  • MODE number Sets the screen mode (0..3).
    • Sets the screen mode and clears the output with CLS. For graphical output, it sets the stroke width.
  • See also: CLS.

MOVE

MOVER

  • MOVER x, y [, p]: Move the graphical cursor relative with offset x,y with optional GRAPHICS PEN p.
  • See also: DRAWR, MOVE, PLOTR.

NEXT

  • NEXT [variable, variable...] Closes a FOR loop. Optional variables are assumed to match open FOR loops.
  • See also: FOR.

ON GOSUB

  • ON index GOSUB line1 [,line2...] Calls subroutine at position index (1-based) in the list.
  • Check GOSUB for how to define a subroutine.
  • If no subroutine matches the index, do nothing.
  • See also: GOSUB.

ORIGIN

  • ORIGIN x,y Sets the origin of the coordinate system for graphical output.
  • See also: DRAW, MOVE, PLOT.

PAPER

  • PAPER number Sets the background color for the text output with PRINT.
  • See also: PEN.

PEN

PLOT

PLOTR

PRINT

  • PRINT [argument1] [; argument2; ...][;] Outputs text and numbers.
    • The semicolor ; suppresses newline. Arguments can be separated by ;.
    • Numbers are padded with trailing space, and leading space for positive numbers.
    • Special operators and functions in PRINT:
      • The comma operator , moves to the next tab position defined by ZONE
      • The TAB function moves to the specified position (only increasing)
      • The SPC function prints the specified number of spaces.
    • In Locobasic, lines can have any length, no automatic newline is inserted.
    • When TAG is active, it creates SVG text.
    • Limitations: Formatting with USING only for one number as with DEC. No additional characters in the format string.

RAD

  • RAD Switches to radians mode (default) for the functions ATN, COS, SIN and TAN.
  • Note: In LocoBasic, the flag is used at compile time starting from its lexical position and not dynamically during execution.
  • See also: DEG.

READ

  • READ variable Reads the next value from DATA into variable.
  • The variable must match the data type.
  • See also: DATA, RESTORE.

REM

  • REM A comment until end of line, same as apostrophe "'".

RESTORE

  • RESTORE [line] Resets the DATA pointer to an optional specified line number or the first DATA line.
  • See also: DATA, READ.

RETURN

STEP

  • FOR... TO... STEP expression Optional STEP count in FOR loops.
  • See also: FOR.

STOP

  • STOP Halts the execution.
    • In LocoBasic: Within subroutines, it functions as a RETURN.
    • Currently the same as END.

TAG

  • TAG Activates text at graphics mode. Following PRINT commands use graphics cursor position and GRAPHICS PEN.
  • Note: In LocoBasic, the flag is used at compile time starting from its lexical position and not dynamically during execution.
  • See also: TAGOFF.

TAGOFF

  • TAGOFF Deactivates text at graphics mode. Following PRINT commands use text positions with text PEN again.
  • Note: In LocoBasic, the flag is used at compile time starting from its lexical position and not dynamically during execution.
  • See also: TAG.

THEN

  • IF expression THEN Part of IF.
  • See also: IF.

TO

  • FOR... TO... Part of FOR loops.
  • See also: FOR.

USING

  • PRINT... USING formatString uses formatString to format print arguments.
  • Allowed characters: ... (TODO).
  • See also: DEC$, PRINT.

WEND

WHILE

  • WHILE expression Control structure: Repeats until expression evaluates to false.
  • Ended with WEND loop.
  • See also: WEND.

WRITE

  • WRITE [argument1] [(;|,) argument2 (;|,) ...][;] Outputs text and numbers. Text is quoted.

Example:

a$="d": WRITE "abc";a$;7 '=> "abc","d",7

ZONE

  • ZONE number Sets the tab zone for the comma operator in PRINT.
  • The default zone is 13.

Operators

AND

  • AND

MOD

  • MOD compute modulus of two numbers.

NOT

  • NOT

OR

  • OR

XOR

  • XOR exclusive-OR.

Functions

ABS

  • ABS(number) Returns the absolute value of number.

ASC

  • ASC(character) Returns the ASCII code of character.
  • See also: CHR$.

ATN

  • ATN(number) Returns the arctangent of the given number.
  • The returned value is in radians or degrees, depending on the active mode RAD or DEG.
  • See also: COS, SIN, TAN.

BIN$

  • BIN$(number [, padding]) Converts a number to its binary representation.
  • See also: HEX$.

CHR$

  • CHR$(number) Returns the character corresponding to the ASCII code number.
  • See also: ASC.

CINT

  • CINT(number) Returns the integer part of number.
  • Same as INT.
  • See also: FIX, INT, ROUND.

COS

  • COS(number) Returns the cosine of the given number.
  • number is interpreted as radians or degrees, depending on the active mode RAD or DEG.
  • See also: ATN, SIN, TAN.

CREAL

  • CREAL(number) Returns number.

DEC$

  • DEC$(number, format) Returns the number as a string formatted according to the specified pattern.
  • Only "#" and "." are supported in the format (no extra characters). Example: "##.###".
  • There is no overflow warning.
  • See also: USING.

EXP

  • EXP(number) Returns e raised to the power of number.
  • See also: LOG.

FIX

FN

  • FNname() or FN name() Call a user defined function defined by DEF FN.
  • If there are no arguments, do not use parentheses.
  • See also: DEF FN.

HEX$

  • HEX$(number [, padding]) Converts a number to its hexadecimal representation.
  • See also: BIN$.

INKEY$

  • INKEY$ Gets the pressed character from the key buffer or an empty string if the buffer is empty.
  • See also: CLEAR INPUT.

INSTR

  • INSTR([startPos,] string1, string2) Returns the first position of string2 in string1, starting at optional startPos.

INT

  • INT(number) Returns the integer part of number.
  • See also: CINT, FIX, ROUND.

LEFT$

  • LEFT$(string, number) Returns number characters from the left of string.
  • See also: RIGHT$, MID$.

LEN

  • LEN(string) Returns the length of the string.
  • LocoBasic has no limitation on the length.

LOG

  • LOG(number) Returns natural logarithm for number (based on e).
  • See also: EXP, LOG10.

LOG10

  • LOG10(number) Returns logarithm for number based on 10.
  • See also: EXP, LOG.

LOWER$

  • LOWER$(string) Returns the string in lowercase.
  • See also: UPPER$.

MAX

  • MAX(number [, number, ...]) Returns the maximum of the given numbers.
  • See also: MIN.

MID$

  • MID$(string, first [, length]) Returns a substring starting at position first with length.
  • a$="abcde": MID$(a$,position,length)="w" When assigning a string to MID$, it modifies the string variable at the given position.
  • See also: LEFT$, RIGHT$.

MIN

  • MIN(number [, number, ...]) Returns the minimum of the given numbers.
  • See also: MAX.

PI

  • PI Returns the value of 'pi'.

POS

  • POS(#0): Returns the current x-position of text output.
  • Supports only stream 0.
  • See also: VPOS.

REMAIN

  • REMAIN(timer) Clears a running timer started with AFTER or EVERY.
  • In LocoBasic it returns the timer ID (and not the remaining time).
  • See also: AFTER, EVERY.

RIGHT$

  • RIGHT$(string, number) Returns number characters from the right of string.
  • See also: LEFT$, MID$.

RND

  • RND[(number)] Returns the next pseudo-random number.
  • In LocoBasic, parameter number is ignored.
  • Use RND directly, not RND(). The parentheses are not part of the function syntax.

ROUND

  • ROUND(number [, decimalPlaces]) Rounds a number to a specified number of decimal places.
  • In LocoBasic, the rounding is not exactly the same as in Locomotive BASIC.
  • See also: CINT, INT, FIX.

SGN

  • SGN(number) Returns the signum of a number (-1, 0, or 1).

SIN

  • SIN(number) Returns the sine of the given number.
  • number is interpreted as radians or degrees, depending on the active mode RAD or DEG.
  • See also: ATN, COS, TAN.

SPACE$

  • SPACE$(number) Returns number spaces.

SPC

  • SPC(number) In PRINT, outputs number spaces before the next argument.
  • See also: PRINT.

SQR

  • SQR(number) Returns the square root of number.

STR$

  • STR$(number) Converts a number to its string representation.
  • A positive number is prefixed with a space.
  • See also: VAL.

STRING$

  • STRING$(number, character | ASCIInumber) Returns character (or CHR$(ASCIInumber)) repeated number times.

TAB

  • TAB(number) In PRINT, outputs the next argument at position number.
  • See also: PRINT.

TAN

  • TAN(number) Returns the tangent of the given number.
  • number is interpreted as radians or degrees, depending on the active mode RAD or DEG.
  • See also: ATN, COS, SIN.

TIME

  • TIME Returns the current system time in 1/300 sec.

UPPER$

  • UPPER$(string) Converts the string to uppercase.
  • See also: LOWER$,

VAL

  • VAL(string) Converts a string to a number.
  • Supports hexadecimal and binary formats.
  • See also: STR$.

VPOS

  • VPOS(#0): Returns the current vertical position / y-position of text output.
  • Supports only stream 0.
  • See also: POS.

XPOS

  • XPOS Returns the x-pos of the current graphical cursor position.
  • See also: YPOS.

YPOS

  • YPOS Returns the y-pos of the current graphical cursor position.
  • See also: XPOS.

Resident System Extensions (RSX)

These are extensions to LocoBasic but can also be implemented on a real CPC with Z80 code.

|ARC

|CIRCLE

|DATE

  • d$=SPACE$(11): |DATE,@d$ Returns a date string in the format "ww DD MM YY" (ww=day of week) from the Real Time Clock (RTC). Use the address operator @ to denote that the result of the RSX command should be written in the variable.
  • Link: Dobbertin Smart Watch.
  • See also: |TIME.

|ELLIPSE

  • |ELLIPSE.cx,cy,rx,ry[,fillPen] Draws an ellipse, creating shape SVG ellipse.
  • lat=0.0: lon=0.0: |GEOLOCATION,@lat,@lon Gets the geolocation (if available).
  • See also: |ARC, |CIRCLE, |POLYGON, |RECT.

|PITCH

  • |PITCH,n Sets the speech synthesis pitch for |SAY (1-20; default: 10)

|POLYGON

|RECT

|SAY

  • |SAY,"Hello" Says "Hello" using speech synthesis.
  • See also: |PITCH.

|TIME

  • t$=SPACE$(8): |TIME,@t$ Returns a time string in the format "HH MM SS" from the Real Time Clock (RTC).
  • Link: Dobbertin Smart Watch.
  • See also: |DATE.

Special Notes

  • Endless Loops

    • The compiled code runs in a Web Worker or Worker Thread. The "Reset" button terminates the Web Worker and also an endless loop.
  • STOP and END

    • These commands stop execution, but only at the top level. Within subroutines, they simply return.
    • During FRAME, INKEY$ or INPUT, the "Stop" button gets active. It allows you to terminate the running program. It is not possible to continue a terminated program.
  • PEN and PAPER

    • When using node.js in a terminal, ANSI colors are used.
  • GRAPHICS PEN, DRAW, DRAWR, MOVE, MOVER, PLOT, PLOTR, TAG (and PRINT), |ARC, |CIRCLE, |ELLIPSE, |POLYGON, |RECT

    • These can be used to create Scalable Vector Graphics (SVG), which can be exported with the "Export SVG" button. Graphics are separate from text.
  • FRAME and Visual Delays

    • Text and graphics output is buffered until it is flushed with FRAME (or INKEY$, INPUT) or at the end of the program.
    • To start new text or graphical output after FRAME, use CLS or MODE.
    • In tight loops, FRAME may execute too fast for visual perception (up to ~50ms per frame).
    • For consistent delays, use: t=TIME+50: WHILE TIME<t: FRAME: WEND
  • INKEY$ Sequential Processing

    • Queued key presses are returned one per call, sequentially.
    • In fast loops, multiple key presses may be processed within a single frame.
  • RND - No Parentheses

    • Use RND directly, not RND(). The parentheses are not part of the function syntax.
  • Text Rendering Without LOCATE

    • LocoBasic has no LOCATE command. Use MID$ assignment for efficient row-based rendering.

    • Example: Build a row string once per frame and redraw:

      row$=SPACE$(40)
      MID$(row$,player,1)="@"
      MID$(row$,enemy,1)="X"
      PRINT row$
      FRAME

Language specification

You may find a full EBNF grammar for LocoBasic in locobasic.ebnf.

Program Structure

  • A program consists of lines:

line ::= [lineNumber] statement { ":" statement }

  • Line numbers are optional except for:

Identifiers

identifier ::= letter { letter | digit | "." }

  • Must start with a letter.
  • May contain letters, digits, or dot ".".
  • Underscore "_" is not allowed.
  • Identifiers are case-insensitive.

Numbers

Supported formats:

  • Decimal: 123
  • Hexadecimal: &FF
  • Binary: &x1011

Strings

Strings are enclosed in quotes: "Hello".

Data Types

Two data types:

  • Numeric
  • String (ending with dollar $).
  • (Not supported: Other type suffixes like !(real) and % (integer)).

Implicit conversion between strings and numbers does not occur. Use STR$(number)or VAL(string).

Arrays

Declared and initialized with DIM.

Indices can be written in parentheses () or brackets [].

  • Note In LocoBasic, array variables with the same name as normal variables are not supported.

Boolean Semantics

  • Logical values: true = -1, false = 0
  • Truth evaluation: ≠ 0 → true

Operator precedence

Predcedences from (highest → lowest):

  • @ (addressOf string)
  • ^
  • unary + -
  • * /
  • \ (integer division)
  • MOD
  • + -
  • = <> < <= > >=
  • NOT
  • AND
  • OR
  • XOR

Operators * / \ MOD + - AND OR XOR are left-associative, ^ is right-associative.

Control Structures

Subroutines

Example:

GOSUB 100
END
'
100 PRINT "HELLO"
RETURN

Grammar notes

Execution model

Implementation Notes

LocoBasic compiles BASIC code to JavaScript.

Consequences:

  • Numeric precision follows JavaScript IEEE‑754 floating point.
  • Programs run inside Web Workers or Node.js worker threads.

Differences from Locomotive BASIC

Not implemented

  • The following keywords are not implemented:

auto border break call cat chain clear clg closein closeout cont copychr$ cursor defreal defstr delete derr di edit ei eof erl err fill fre goto graphicsPaper himem inkey inp joy line list load locate mask memory merge new openin openout out peek poke randomize release renum resume run save sound speed sq swap symbol test testr troff tron unt wait width window

  • GOTO, ON...GOTO are not supported.
  • Nested subroutines are not supported.
  • LOCATE is not supported.
  • Text windows (WINDOW) are not supported.
  • Streams using prefix '#' (e.g. PRINT, CLS, INPUT) are not supported.
    • Exception: POS(#0), VPOS(#0) with stream 0.

Command line options and URL parameters

  • action=compile,run -- Possible actions: compile,run (compile and run) or compile (compile only and output script)
  • autoCompile=true -- Automatic compile to JavaScript when BASIC code changes (Browser)
  • autoExecute=true -- Automatic execute when JavaScript code changes (Browser)
  • databaseDirs=examples -- Example base directories (comma separated)
  • database=examples -- Selected database available in databaseDirs, e.g. examples, apps, saved
  • debounceCompile=800 -- Debounce delay for autoCompile in ms (Browser)
  • debounceExecute=400 -- Debounce delay for autoExecute in ms (Browser)
  • debug=0 -- Debug level
  • example=locobas -- Selected example in the current database
  • exampleFilter= -- Example filter value for the search field (Browser)
  • fileName= -- Specify a BASIC file (nodeJS)
  • grammar=basic -- Possible options: basic or strict
  • input= -- Specify a BASIC string (nodeJS)
  • outputConsole=false -- Redirect output to the Browser Devtools console
  • showBasic=true -- Show the BASIC editor (Browser)
  • showCompiled=false -- Show the JavaScript editor (Browser)
  • showOutput=true -- Show the output box (Browser)

Links

mv, 03/2026

Releases

No releases published

Packages

 
 
 

Contributors