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)
GOTOis intentionally not supported
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.
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.jsLocoBasic 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.
AFTER timeout [, timer] GOSUB lineCalls 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.
CLSClears the output window.- Note: In LocoBasic, PEN, PAPER, GRAPHICS PEN and TAG are also initialized.
- See also: MODE.
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 FNname[(arg1, ...)] = expressionDefines a user defiend function FNname.- The defined function can be called as
FNname()orFN name(). - If there are no arguments, do not use parentheses.
- See also: FN.
DEFINT aDefines, 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.
DEGSwitches 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 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 x, y [, p]: Draw a line to position x,y with optional GRAPHICS PEN p.- See also: DRAWR, MOVE, PLOTR.
DRAWR x,y [, p]: Draw a line relative with offset x,y with optional GRAPHICS PEN p.- See also: DRAW, MOVER, PLOTR.
ENDEnds execution.- This is currently the same as STOP.
ENDIFEnds a multi-line IF. (Only supported in LocoBasic.)
ERASE variable, [variable,...]Erases array variables.- Specify variable name without indices.
ERROR numberThrows an error with number.
EVERY timeout [, timer] GOSUB lineCalls 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 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.
FRAMEPauses 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: WENDGOSUB lineCalls subroutine starting at line.- Subroutines must end with a single RETURN on its own line.
GRAPHICS PEN numberSets the graphics pen (0..15)
IF expression THEN statements [ELSE statements]control structure (in one line).
INK pen,colorSets 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 [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 78,1,kCreates 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 [message;] variablePrompts the user for a line of input (string).- See also: INPUT.
MODE numberSets 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 x, y [, p]: Move the graphical cursor to position x,y with optional GRAPHICS PEN p.- See also: DRAWR, MOVER, PLOT.
MOVER x, y [, p]: Move the graphical cursor relative with offset x,y with optional GRAPHICS PEN p.- See also: DRAWR, MOVE, PLOTR.
NEXT [variable, variable...]Closes a FOR loop. Optional variables are assumed to match openFORloops.- See also: FOR.
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 x,ySets the origin of the coordinate system for graphical output.- See also: DRAW, MOVE, PLOT.
PEN numberSets the color for the text output with PRINT.- For the terminal, ANSI escape codes for colors are used.
- See also: PAPER.
PLOT x, y [, p]: Plot a point at position x,y with optional GRAPHICS PEN p.- See also: DRAW, MOVE, PLOTR.
PLOTR x, y [, p]: Plot a point relative with offset x,y with optional GRAPHICS PEN p.- See also: DRAWR, MOVER, PLOTR.
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: - 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.
- The semicolor
RADSwitches 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 variableReads the next value from DATA into variable.- The variable must match the data type.
- See also: DATA, RESTORE.
REMA comment until end of line, same as apostrophe "'".
RESTORE [line]Resets the DATA pointer to an optional specified line number or the first DATA line.- See also: DATA, READ.
FOR... TO... STEP expressionOptionalSTEPcount inFORloops.- See also: FOR.
STOPHalts the execution.
TAGActivates 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.
TAGOFFDeactivates 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.
FOR... TO...Part ofFORloops.- See also: FOR.
PRINT... USING formatStringuses formatString to format print arguments.- Allowed characters: ... (TODO).
- See also: DEC$, PRINT.
WHILE expressionControl structure: Repeats until expression evaluates to false.- Ended with WEND loop.
- See also: WEND.
WRITE [argument1] [(;|,) argument2 (;|,) ...][;]Outputs text and numbers. Text is quoted.
Example:
a$="d": WRITE "abc";a$;7 '=> "abc","d",7- See also: PRINT.
ZONE numberSets the tab zone for the comma operator in PRINT.- The default zone is 13.
AND
MODcompute modulus of two numbers.
NOT
OR
XORexclusive-OR.
ABS(number)Returns the absolute value of number.
ASC(character)Returns the ASCII code of character.- See also: CHR$.
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$(number [, padding])Converts a number to its binary representation.- See also: HEX$.
CHR$(number)Returns the character corresponding to the ASCII code number.- See also: ASC.
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(number)Returns number.
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(number)Returns e raised to the power of number.- See also: LOG.
FNname()orFN name()Call a user defined function defined by DEF FN.- If there are no arguments, do not use parentheses.
- See also: DEF FN.
HEX$(number [, padding])Converts a number to its hexadecimal representation.- See also: BIN$.
INKEY$Gets the pressed character from the key buffer or an empty string if the buffer is empty.- See also: CLEAR INPUT.
INSTR([startPos,] string1, string2)Returns the first position of string2 in string1, starting at optional startPos.
LEN(string)Returns the length of the string.- LocoBasic has no limitation on the length.
LOWER$(string)Returns the string in lowercase.- See also: UPPER$.
MAX(number [, number, ...])Returns the maximum of the given numbers.- See also: MIN.
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(number [, number, ...])Returns the minimum of the given numbers.- See also: MAX.
PIReturns the value of 'pi'.
POS(#0): Returns the current x-position of text output.- Supports only stream 0.
- See also: VPOS.
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.
RND[(number)]Returns the next pseudo-random number.- In LocoBasic, parameter number is ignored.
- Use
RNDdirectly, notRND(). The parentheses are not part of the function syntax.
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(number)Returns the signum of a number (-1, 0, or 1).
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$(number)Returns number spaces.
SQR(number)Returns the square root of number.
STR$(number)Converts a number to its string representation.- A positive number is prefixed with a space.
- See also: VAL.
STRING$(number, character | ASCIInumber)Returns character (orCHR$(ASCIInumber)) repeated number times.
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.
TIMEReturns the current system time in 1/300 sec.
UPPER$(string)Converts the string to uppercase.- See also: LOWER$,
VAL(string)Converts a string to a number.- Supports hexadecimal and binary formats.
- See also: STR$.
VPOS(#0): Returns the current vertical position / y-position of text output.- Supports only stream 0.
- See also: POS.
XPOSReturns the x-pos of the current graphical cursor position.- See also: YPOS.
YPOSReturns the y-pos of the current graphical cursor position.- See also: XPOS.
These are extensions to LocoBasic but can also be implemented on a real CPC with Z80 code.
|ARC,x,y,rx,ry,angle,large-arc-flag,sweep-flag,x,y[,fillPen]Draws an arc curve, creating shape SVG Elliptical arc curve.- See also: |CIRCLE, |ELLIPSE, |POLYGON, |RECT.
|CIRCLE.cx,cy,r[,fillPen]Draws a circle, creating shape SVG circle.- See also: |ARC, |ELLIPSE, |POLYGON, |RECT.
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.cx,cy,rx,ry[,fillPen]Draws an ellipse, creating shape SVG ellipse.lat=0.0: lon=0.0: |GEOLOCATION,@lat,@lonGets the geolocation (if available).- See also: |ARC, |CIRCLE, |POLYGON, |RECT.
|PITCH,nSets the speech synthesis pitch for |SAY (1-20; default: 10)
|POLYGON,x1,y1,...,xn,yn[,fillPen]Draws a polygon, creating shape SVG polygon.- See also: |ARC, |CIRCLE, |ELLIPSE, |RECT.
|RECT,x,y,x2,y2[,fillPen]Draws a rectangle, creating shape SVG rect.- See also: |ARC, |CIRCLE, |ELLIPSE, |POLYGON.
|SAY,"Hello"Says "Hello" using speech synthesis.- See also: |PITCH.
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.
-
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,
FRAMEmay 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
RNDdirectly, notRND(). The parentheses are not part of the function syntax.
- Use
-
Text Rendering Without LOCATE
-
LocoBasic has no
LOCATEcommand. UseMID$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
-
You may find a full EBNF grammar for LocoBasic in locobasic.ebnf.
- A program consists of lines:
line ::= [lineNumber] statement { ":" statement }
- Line numbers are optional except for:
identifier ::= letter { letter | digit | "." }
- Must start with a letter.
- May contain letters, digits, or dot ".".
- Underscore "_" is not allowed.
- Identifiers are case-insensitive.
Supported formats:
- Decimal:
123 - Hexadecimal:
&FF - Binary:
&x1011
Strings are enclosed in quotes: "Hello".
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).
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.
- Logical values: true = -1, false = 0
- Truth evaluation: ≠ 0 → true
Predcedences from (highest → lowest):
Operators * / \ MOD + - AND OR XOR are left-associative, ^ is right-associative.
- Called with GOSUB
Example:
GOSUB 100
END
'
100 PRINT "HELLO"
RETURNLocoBasic compiles BASIC code to JavaScript.
Consequences:
- Numeric precision follows JavaScript IEEE‑754 floating point.
- Programs run inside Web Workers or Node.js worker threads.
- 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.
- 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)
-
Ohm JavaScript parsing toolkit - Source code - Paper: Modular Semantic Actions
-
CodeMirror code editor used for the LocoBasic UI - Source code - cdnjs Libraries
-
CPCBasicTS CPCBasicTS Unchained - Run CPC BASIC in a Browser - Source code
-
CPCBasic CPCBasic Unchained - Run CPC BASIC in a Browser - Source code
-
CPCemu - CPC Emulator, since version 2.0 with very accurate emulation
-
Amstrad CPC 6128 User Instructions, or: Schneider CPC 6128 Benutzerhandbuch
-
Locomotive BASIC - Description of the CPC Basic Dialect
-
GodSVG Editor - Edit SVG drawings
-
Another BASIC compiler: ugBASIC