Table of Contents
Tiny BASIC Web, version 0.8
©2025 by Mark Damon Hughes
cyberhole.online/basic/
Text licensed as CC BY-NC-SA Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International license
Code licensed as BSD, see LICENSE.txt
Tiny BASIC Web (TBW hereafter) is an interpreted, interactive BASIC which can be embedded in a web page. The purpose is to make interactive menus and mini-games easy to deploy. It's not a fully-baked programming environment, and not usable on command-line, both of which exist in many other places.
Level 1 supported only numeric or string variables of 1-2 chars, string constants. No arrays, functions, labels, or optional items. RND is a pseudo-variable.
Level 1 terminal only supports one physical line for commands or inputs, so maximum 63 chars in my setup. Whether this will be improved in Level 2, or only in uploaded scripts, remains to be seen. Ideally it should support lines of at least 120 chars, maybe 255.
Level 2 is as below.
Just type. In command mode (after READY or any successful command), up to a screen width of characters can be entered.
Paste (⌘V or ^V) works, but is subject to the same rules as keyboard use: Wrap your lines at 63 chars.
To get your program out, use LLIST, and copy from the "printer" listing that opens below.
You can find out what files exist with DIR, and choose to load one with LOAD "filename".
Once you have a program entered, type RUN.
NEW or reload the page to get a blank BASIC again.
While running, you can hit ^Z to break out of the program!
<html>
<head>
<title>TinyBasicWeb</title>
<link href='../style.css' rel='stylesheet' type='text/css' />
<script src='../js/mdhutil.js'></script>
<script src='../js/terminal.js'></script>
<script src='../js/tbasicweb.js'></script>
</head>
<body onload='tbasicwebinit("terminal", [64,24], [15,24], "myprog.bas")'>
<div id='terminal'></div>
</body>
</html>
You can start with no program ("boot to READY") by changing "myprog.bas" to null.
Or use a 'run' URL parameter as your BASIC program, change "myprog.bas" to
new URL(document.location).searchParams.get("run"))
To pack the canvas size correctly, pick sizes you like, run it. The browser console will show Actual charsize=X. Then change the ones you give in the function, and reload. My initial guess of [64,24], [14,24] produced Actual charsize=14.40234375, so I changed width to 15.
{} is grouping, [] is optional, … is 0 or more, | separates options. Case is ignored, but in this grammar UPPER is keywords, lower is syntax term.
linenum := 1-32767
line := linenum statement
label := linenum
label := linenum|namename := a-z {a-z|0-9}… : Names can be maximum (L1) 2 chars, (L2) 16 chars.
number := [+|-]0-9… [. 0-9…] [E[+|-]0-9…]
string := " [any char except "]… "
varname := name [$][( term {, term}… )] : name cannot otherwise be a reserved word.
value := number|string
term := number|string|varname|(expr) : All expressions must be parenthesized.
op := {+ | - | * | / | % | ^ | = | <> | < | <= | > | >=} :
% is integer modulo, ^ is exponent.+ appends, <> | < | <= | > | >= compares case-insensitively. Appending a number to a string (even empty "") makes the number a string: (""+13) = "13".expr := term {op term}… : Note no operator precedence except parens! Strict left-to-right.
(L2) channel := # 0-255
Note: - cannot be prefix, use (x*-1) to negate.
Boolean logic: Done with 0 & 1 values. A OR B := (A+B), A AND B := (A*B), FALSE A := (A=0), TRUE A := (A<>0).
Use + as Boolean OR, * as Boolean AND.
| + OR | | | * AND | ||||
|---|---|---|---|---|---|---|
| x | y | | | x | y | ||
| . | 0 | 1 | | | . | 0 | 1 |
| 0 | 0 | 1 | | | 0 | 0 | 0 |
| 1 | 1 | 2 | | | 1 | 0 | 1 |
statement := keyword args, see below
REM or ' : Starts a comment, runs to end of line.END : End program.GOSUB label : Temporarily jumps to any line number or label.GOTO label : Jumps to any line number or label.IF term THEN statement1 : Tests term, if non-zero executes statement1. (L2) [ELSE statement2] executes statement2 if term was zero.INC varname, DEC varname : Increment or decrement varname, like LET v=(v+1).INPUT ["PROMPT",] varname : Print prompt or "?", request one value for varname.LET varname = term : Set variable to term. Note any math expression must be parenthesized.
STR$).VAL).{, varname = term}… : L2 allows multiple assignments on a line.{PRINT|?} [channel ,] {term {{,|;} term}… [{,|;}] } : Followed by terms, separated by semicolon ; (no spacing) or comma , (which inserts a tab). (L2) channel.LPRINT ... : as PRINT, but to line printer. Graphics commands do not work in LPRINT, only ; and ,RETURN : Returns from GOSUB, error if not in a GOSUB. Use a variable like rc to return values.DIM varname : Dimension array variable, e.g. DIM A(10).
{, varname}… : L2 allows multiple dimensions on a line.PAUSE s : Pauses execution for s seconds, which may be fractional.(L2)
(L2) FOR varname = term1 TO term2 [STEP term3] : Starts a loop, sets var to term1, pushes to stack "varname term2 term3 linenum". Continued by NEXT or terminated by BREAK.
(L2) NEXT varname : If top of stack is varname loop, increments by term3, IF varname <= (if term3 is positive) or >= term2 (if term3 is negative) THEN GOTO linenum ELSE pop from stack.
(L2) * label : Sets a label. Must be unique across the program.
(L2) BREAK : Prematurely ends most recent FOR loop.
(L2) ERROR "why" : Error with code 255, message "why".
(L2) TRAP label : Any error does a GOTO label. TRAP negative number disables it.
(L2) ON term GOTO label1 {, labelN}... : Use term, as integer, to select label to GOTO, from 1 to N. Falls through to next line if term<1 or term>N.
(L2) ON term GOSUB label1 {, labelN}... : As above, but GOSUB.
(L2) DATA value {, value}… : Store data. Note strings must be quoted. DATA isn't strictly needed, use FOPEN, FEOF, FGET to read lines from remote.
(L2) READ varname : Read next data value & advance pointer.
(L2) RESTORE [label] : Reset data pointer to start of program or specific label.
(L2) RANDOMIZE n : Set random number seed to n. Optional since RND is seeded with time at start.
NEW : Clear program & memory.RUN ["filename"] : Clear memory. If filename is given, load it (see LOAD below). Start program at first line.LIST [A [, B]] : List program, from labels A to B.LLIST ... : as LIST, but to line printer.(L2)
CLEAR : Clear memory.DEL A [, B] : Delete line, or lines in range.EDIT label : Put program line in the input buffer.MEMO : Enter memo mode, can move around screen and "draw" by typing."foo.ext" is a filename. Name can be up to 16 chars (A-Za-z0-9_.- only, only letter or digit initial) long, case-insensitive (lowercased on server), extension should be "bas" for BASIC, but can be other types for other files.
Regular expression: /^[A-Za-z0-9][A-Za-z0-9_.-]{0,15}$/
Use LOCK and UNLOCK to protect your programs when you're not working on them.
DIR ["filespec"] : List files in TBW/disk that match text in filename. "" or no args matches all files.LOAD "filename" : Clear program & memory, load file TBW/disk/filename.REDIR url$ : Redirect current page to URL.(L2)
REMOTE url$, stvar, textvar : Sends URL request, store HTTP status in stvar, response text in textvar. For complex returns, use something like:
10 REM url$ returns a DOS filename.
11 REMOTE url$, st, v$
12 IF (st<>200) THEN ERROR ("Network error "+st)
13 REM or use FOPEN, FGET to read file.
14 DEL 20000,20999
15 MERGE v$
16 GOSUB 20000
ERASE "filename" : Erase file TBW/disk/filename. Error if file is locked.
FOPEN channel, "filename", "mode" : Open file TBW/disk/filename in mode, which is "r" (read), "w" (write), "a" (append). "w" and "a" error if file is locked.
FEOF channel, varname : Set varname to 1 if EOF, 0 if more data.
FPUT channel, term : Write value to channel with newline.
FGET channel, varname : Read one line from channel to varname.
FCLOSE channel : Close & flush channel.
LOCK "filename", "password": Locks a filename with a secret password, which is also up to 16 chars long.
MERGE "filename" : Load file TBW/disk/filename, merging lines.
SAVE "filename" : Save program to file TBW/disk/filename. Error if file is locked.
UNLOCK "filename", "password": Unlocks.
Parentheses are optional for 0 arguments, so RND can be treated as a "pseudo-variable", giving a new random number every time it is referenced.
ASC(a$) : Unicode value of first character in a$.
ATN(r), COS(r), SIN(r), TAN(r) : Arctangent, cosine, sine, tangent of radians r.
((d*PI)/180) to convert degrees to radians, (r*180/PI) to convert radians to degrees.ABS(n) : Absolute value of n.
CHR$(n) : String containing a Unicode character.
EXP(n), LOG(n), SQR(n) : Exponent, natural log, square root of n. Any invalid value like SQR(-1) returns 0.
INKEY$() : Last key pressed. Some keys return special values:
| Key | INKEY$ | Display |
|---|---|---|
| Backspace | CHR$(8) | (backspace) |
| Tab | CHR$(9) | (tab) |
| Enter | CHR$(10) | (newline) |
| Escape | CHR$(27) | |
| Delete | CHR$(127) | |
| Insert | CHR$(26) | |
| Home | CHR$(28) | |
| End | CHR$(29) | |
| PageUp | CHR$(30) | |
| PageDown | CHR$(31) | |
| Up | CHR$(8593) | ↑ |
| Right | CHR$(8594) | → |
| Down | CHR$(8595) | ↓ |
| Left | CHR$(8592) | ← |
INSTR(h$,n$,x) : Returns the index of n$ (needle) in h$ (haystack), starting at x (defaults to 1), starting at 1, case-sensitive. 0 is not found.
INT(n) : Truncates n to integer portion.
LEN(a$) : Length of a string.
MID$(a$,i,n) : Substring of a$ from i through i+n-1 indices, starting at 1.
MID$(a$,1,n).MID$(a$,(LEN(a$)-n+1),n).PI() : Value of π.
RND() : Random number from 0.0 to 1.0 (not inclusive)
STR$(n) : String of a number. Can also do (""+n).
STRCMP(a$, b$): Case-sensitively compares a$ to b$, returns -1 if less than, 0 if equal, 1 if greater than.
UCASE$(a$), LCASE$(a$) : Convert string to uppercase, lowercase.
TIMER(), DATE$(), TIME$() : Current time in float seconds since epoch, local date & time in ISO8601, UTC.
VAL(a$) : Numeric value of a string, 0 if not valid. Can also assign to a numeric var.
(L2)
ERL(), ERN(), ERM$() : Error line, number, message.Low-rez mixes character and "pixel" graphics. Characters are placed on the screen from 0,0 to LOC(2), LOC(3) positions. Use PRINT AT(x,y) to specify location.
Pixel graphics (px,py) have 4 pixels in a 2x2 grid per character, use LOC(2)*2, LOC(3)*2 to get screen size in pixels.
PIX$(n) lets you mix text and graphics, or store a line of pixels in a string.
Drawing characters or pixels offscreen is ignored.
Statements:
CLS : Clear screen, sets background to the current BG color!
If you need to force white on black again,
10 ?COLOR(15,0);
11 CLS
CIRCLE px,py,pr,a1,a2 : Draw pixel circle, radius pr, from radians a1 to a2 (0,0 to get a full circle).DRAWTO px,py : Draw pixel line.PLOT px,py : Plot pixel.SOUND dur,freq,vol,type : Play simple sound in background. dur is seconds, freq is frequency in hertz (C is 440), vol is volume from 0.0 to 1.0, type is 0:sawtooth, 1:sine, 2:square, or 3:triangle.
SOUND 0.25,440,0.5,0
Functions:
AT(x,y) : Moves cursor to char coordinate x,y, returns "", useful in PRINT.
LOC(n) : Coordinates: n=0: x-coord; n=1: y-coord; n=2: screen width; n=3: screen height; n=4: Unicode value of char under cursor; n=5: FG color of char under cursor; n=6: BG color of char under cursor; other: 0.
TAB(x) : Moves the cursor to column x (0 to LOC(3)-1), returns "": PRINT TAB(16);"X";TAB(32);"Y". Note TAB does not work in LPRINT (no cursor to move!).
COLOR(fg,bg) : Sets color fg (foreground), bg (background), returns "", useful in PRINT. COLOR(15,0) resets to normal. Example:
PRINT AT(32,0);COLOR(9,0);PIX$(9);PIX$(6);"EMERGENCY!";PIX$(6);PIX$(9);COLOR(15,0);AT(0,1);
| COL | NAME | HEX |
|---|---|---|
| 0 | black | #000000 |
| 1 | maroon | #800000 |
| 2 | green | #008000 |
| 3 | olive | #808000 |
| 4 | navy | #000080 |
| 5 | purple | #800080 |
| 6 | teal | #008080 |
| 7 | gray | #808080 |
| 8 | silver | #C0C0C0 |
| 9 | red | #FF0000 |
| 10 | lime | #00FF00 |
| 11 | yellow | #FFFF00 |
| 12 | blue | #0000FF |
| 13 | magenta | #FF00FF |
| 14 | cyan | #00FFFF |
| 15 | white | #FFFFFF |
| 16 | greydark | #404040 |
| 17 | brown | #804000 |
| 18 | orange | #FF8000 |
PIX$(n) : Returns Pixel char: n=0-15 with lit quadrants 1 2 / 4 8:
00=#x20 01=#x2598 ▘ 02=#x259d ▝ 03=#x2580 ▀
04=#x2596 ▖ 05=#x258c ▌ 06=#x259e ▞ 07=#x259b ▛
08=#x2597 ▗ 09=#x259a ▚ 10=#x2590 ▐ 11=#x259c ▜
12=#x2584 ▄ 13=#x2599 ▙ 14=#x259f ▟ 15=#x2588 █