A complete JavaScript engine and runtime environment for classic Amiga
computers. Built from scratch in C, targeting MC68000 and AmigaOS 2.0+.
Inspired by Node.js, adapted for the Amiga platform.
Features
--------
* JavaScript engine (ES5.1 + ES6+ features)
- Arrow functions, classes with inheritance, super(), static
- Class fields: public, private (#field), static
- Template literals, tagged
- Destructuring, spread/rest, default parameters
- for-of loops, labeled break/continue
- Regex literal /pattern/flags
- Optional chaining (?.), nullish coalescing (??)
- BigInt (123n, arithmetic, comparison, BigInt())
- let/const block scoping, numeric separators
- async/await, Promises, dynamic import()
- Generators (function*, yield, yield* delegation)
- Async generators (async function*, for await...of)
- Generator methods in classes and objects
- Automatic Semicolon Insertion (ASI)
- Closures, prototype chain
* Built-in objects and functions
- console (log, error, warn, dir, time/timeEnd, table,
count/countReset)
- Math (27 functions incl. cbrt/fround/clz32/imul)
- JSON (with reviver/replacer)
- Date (full API incl. UTC, getUTC*/setUTC*)
- RegExp (lookahead, non-capturing groups, quantifiers, lazy,
named groups, lookbehind, flags g/i/m/s/u/y)
- Buffer (hex/base64)
- Promise (then/catch/finally/all/race/allSettled)
- Map, Set, WeakMap, WeakSet, Symbol
- setTimeout/setInterval/clearTimeout
- crypto (MD5, SHA-256), structuredClone
- TextEncoder, TextDecoder
- Typed Arrays: Uint8Array, Int8Array, Uint16Array, Int16Array,
Uint32Array, Int32Array, Float32Array, Float64Array,
Uint8ClampedArray, ArrayBuffer, DataView
(from/of, forEach/map/filter/find/every/some/reduce,
set/fill/slice/subarray/reverse/indexOf/includes/join)
- fetch() global HTTP client
- performance.now() timer
- Error.cause (ES2022)
- process.stdout.write, process.stdin.read
- process.hrtime, process.nextTick, queueMicrotask
* String methods: 29+ (charAt, indexOf, slice, split, replace,
replaceAll, trim, includes, startsWith, padStart, normalize,
localeCompare, codePointAt, etc.)
* Array methods: 37+ (push, pop, map, filter, reduce, reduceRight,
sort, find, findLast, findLastIndex, forEach, every, some, splice,
flat, flatMap, entries, keys, values, copyWithin, at,
Array.from/of/isArray, toReversed, toSorted, toSpliced, with)
* Object: keys, values, entries, assign, create, freeze, seal, is,
fromEntries, defineProperty/ies, hasOwn, getPrototypeOf,
setPrototypeOf, getOwnPropertyNames,
getOwnPropertyDescriptor(s), preventExtensions, isExtensible,
groupBy, getOwnPropertySymbols, getters/setters
* Number: isInteger, isFinite, isNaN, isSafeInteger, parseInt,
parseFloat, toString(radix), toFixed, toExponential, toPrecision,
constants (EPSILON, MAX_SAFE_INTEGER, MIN_SAFE_INTEGER, NaN,
Infinity)
* Global functions: encodeURIComponent, decodeURIComponent,
encodeURI, decodeURI, parseInt, parseFloat, isNaN, isFinite,
eval(), fetch()
* Symbol: Symbol(), Symbol.for/keyFor, well-known symbols, Symbol
as property key, Iterator protocol (Symbol.iterator)
* Amiga Intuition GUI bindings
- Open windows with title, size, position
- Graphics: lines, rects, circles, text, pixels
- Mouse and keyboard events
- System requesters (alert, confirm)
- Screen information query
* GadTools GUI toolkit (gui module)
- createWindow() with declarative gadget layout
- 12 widget types: button, string, text, integer, checkbox,
cycle, slider, listview, mx, number, scroller, area
(bevel box)
- gui.gfx: drawing on GadTools windows (setColor, drawLine,
fillRect, drawCircle, drawText, setPixel, setFont, etc.)
- innerSize() for drawable area, auto GZZ offset
- Resizable windows (GIMMEZEROZERO clipping)
- Percentage layout (left: "50%", width: "80%")
- Negative positioning (top: -20 = anchor bottom)
- flex: true - gadget expands to fill available space
- minWidth/minHeight
- font: { name, size } per-window custom font (e.g.
topaz.font 8 for fixed-width column alignment)
- Event-driven: waitEvent/pollEvent, gadgetup/gadgetdown/
mousemove/close/key
- evt.x, evt.y mouse coords on gadget events
- get/set gadget values at runtime
- setTitle, listview dynamic item update
- Menu bar: setMenu() with keyboard shortcuts
- ASL file requester: fileRequest() with patterns
- ASL font requester: fontRequest()
- Proper GadTools cleanup
* Clipboard module (clipboard)
- read() - read text from system clipboard
- write(text) - write text
- clear() - clear clipboard
- Uses IFF FTXT/CHRS via iffparse.library
* ARexx module (arexx)
- send(port, command) - send ARexx command to app
- createPort(name) / closePort()
- getMsg() / waitMsg() / reply()
- listPorts() - list all public message ports
- Uses rexxsyslib.library
* FFI module (amiga)
- Direct Amiga library function calls from JavaScript
- openLibrary(name, version) / closeLibrary(lib)
- call(lib, offset, regs) - call any library function with
register args
- peek8/16/32(addr), poke8/16/32(addr, val) - memory access
- peekString(addr), pokeString(addr, str)
- allocMem(size, flags) / freeMem(ptr, size)
- makeTags(array) - build TagItem array for Amiga tag-based APIs
- Full 32-bit unsigned support (0xDEADBEEF etc.)
* Interactive debugger (--debug)
- Step, next, step-out, continue
- Breakpoints by line number
- Variable inspection (p varname)
- Call stack backtrace (bt)
- Source listing around current line
* Node.js-compatible modules
- require() with caching
- CommonJS exports (module.exports)
- ES modules: import/export (default, named, namespace)
- Dynamic import() expression
- Native modules: fs, http, net, buffer, os, child_process,
crypto, intuition, gui, readline, dns, clipboard, arexx,
amiga
- JS modules: path, events, url, querystring, util, stream,
assert, repl, timers, string_decoder, punycode, console,
constants, zlib, iff
* File I/O (fs module)
- readFileSync, writeFileSync, appendFileSync
- existsSync, unlinkSync, renameSync, statSync
- readdirSync, mkdirSync, rmdirSync, copyFileSync
- readFile, writeFile (async)
- createReadStream, createWriteStream
- Full AmigaOS path support (Volume:dir/file)
* HTTP/HTTPS server and client
- http.createServer() non-blocking, 8 concurrent
- http.get(), http.post(), http.request()
- HTTPS via AmiSSL (build with -DENABLE_AMISSL)
- Auto redirect following (301/302/307)
- 10-second socket timeout, dns.lookup()
- Requires bsdsocket.library (Roadshow, Miami, AmiTCP)
* OS integration
- process.platform ("amigaos"), process.arch ("m68k")
- process.argv, process.env, process.cwd(), process.exit
- os.cpus() (detects 68000/020/030/040/060)
- os.freemem(), os.version() (Kickstart version)
- child_process.execSync() via SystemTagList()
* Interactive REPL with command history (arrow up/down)
* Error types: Error, TypeError, RangeError, ReferenceError,
SyntaxError, EvalError, URIError
* Error messages with filename and line number
* Compiler: bundle JS + interpreter into standalone executable
- NodeAmiga -compile MyApp script.js
- Automatic require() and import dependency resolution
- All JS modules bundled into single executable
- No NodeAmiga, no libs/ needed to run the output
Usage
-----
NodeAmiga script.js
Execute a JavaScript file
NodeAmiga -e "code"
Evaluate inline JavaScript
NodeAmiga -compile out script
Compile to standalone exe
NodeAmiga
Start interactive REPL
NodeAmiga -help
Show help
NodeAmiga -v
Show version
NodeAmiga --tree script.js
Use tree-walking interpreter
NodeAmiga --debug script.js
Run with interactive debugger
NodeAmiga --ast script.js
Print AST (debug)
Installation
------------
1. Copy NodeAmiga to C: or any directory in your path
2. Copy the libs/ directory to PROGDIR: (same directory as
NodeAmiga), or to LIBS:node/
3. Optional: copy examples/ for demo scripts
Quick test:
NodeAmiga -e "console.log('Hello Amiga!')"
NodeAmiga examples/hello.js
NodeAmiga examples/gui_hello.js
NodeAmiga examples/fibonacci.js
Compile to standalone exe:
NodeAmiga -compile Hello examples/hello.js
Hello
; Runs without NodeAmiga or libs/ !
System Requirements
-------------------
* MC68000 or higher CPU
* AmigaOS 2.04 (Kickstart 37) or higher
* Minimum 1 MB RAM (2+ MB recommended)
* bsdsocket.library for networking features (Roadshow, Miami,
AmiTCP)
* No FPU required (software floating-point)
Technical Details
-----------------
* Engine: custom-built lexer, recursive descent parser with Pratt
precedence climbing, bytecode VM (default) with tree-walking
fallback
* Bytecode VM: ~8x faster than tree-walking, lazy compilation,
direct native dispatch
* Memory: reference counting (no GC pauses), arena allocator for
AST, integer cache 0-255
* Stack: auto-allocates 64 KB via StackSwap
Included Examples
-----------------
hello.js
Platform info and greeting
fibonacci.js
Recursive/iterative benchmark
file_io.js
File read/write/append
http_server.js
HTTP server with routing
http_client.js
HTTP GET and POST requests
classes.js
ES6 classes with inheritance
closures.js
Closures, IIFE, module pat.
array_fun.js
map/filter/reduce/sort
regex.js
RegExp /literal/ and methods
events.js
EventEmitter custom events
promises.js
Promise chains, all, race
timers.js
setTimeout and setInterval
buffer.js
Binary data manipulation
streams.js
Readable/Writable/Transform
sysinfo.js
Full system info display
todo_app.js
TODO app with persistence
mini_grep.js
Pattern search in files
url_parser.js
URL and query string parsing
calculator.js
Recursive descent expr parser
json_db.js
JSON file-based database
myip.js
Fetch public IP from internet
nettime.js
Internet time via HTTP API
weather.js
Weather info for a city
download.js
Download file and save
gui_hello.js
Simplest Intuition window
gui_demo.js
Interactive drawing + mouse
gui_calculator.js
GadTools calculator (320x200)
3d.js
Rotating 3D wireframe cube
3d_tunnel.js
Wireframe tunnel fly-through
pong.js
Bouncing ball (XOR, circle)
gui_translator.js
Google Translate with GUI
gui_window.js
GadTools widget gallery
gui_menu.js
Menu bar + ASL requesters
gui_drawing.js
gui.gfx primitives (4 scenes)
gui_gfx_clock.js
Analog clock (resizable)
gui_keyboard_and_scrolls.js
Arrow keys + scrollers demo
gui_weather.js
Weather app with GadTools GUI
aminet_browser.js
Aminet archive browser + GUI
clipboard.js
System clipboard read/write
arexx.js
ARexx port communication
dns_lookup.js
DNS hostname resolution
readline_chat.js
Interactive readline + prompt
ffi.js
Amiga library FFI demo
iff_info.js
IFF/ILBM/8SVX file viewer
Known Limitations
-----------------
* No Proxy
* await is synchronous (spin-waits on event loop)
Version History
---------------
0.20.0 (2026-04-17)
- FIX: Strict equality 0 === "0" wrongly returned true with
tagged ints (now correctly false)
- FIX: Loose equality 0 == null wrongly returned true with
tagged ints (now correctly false per spec)
- FIX: typeof on tagged ints could crash (defensive check
added in js_typeof)
- FIX: Promise.finally() was implemented as .then(cb,cb)
which dropped the original value -- now properly passes
through resolved value or rejection reason
- FIX: Promise.finally() use-after-free when callback
threw (released thrown value before reject_promise
could retain it)
- NEW: String.prototype.toString() and valueOf() (were
missing from prototype registration)
- NEW: Number.prototype.valueOf()
- NEW: gui.createWindow accepts font: { name, size }
option for per-window fixed font (e.g. topaz.font 8 for
proper column alignment in listviews)
- aminet_browser.js v1.4: Recent button (browses
/recent), Find renamed from Search All, version check
on startup, custom topaz 8 font for column alignment
0.19.0 (2026-04-16)
- NEW: FFI module (require('amiga')) - call any Amiga library
function from JavaScript
- NEW: openLibrary/closeLibrary, call with register args
- NEW: peek8/16/32, poke8/16/32, peekString, pokeString
- NEW: allocMem/freeMem, makeTags for tag-based APIs
- NEW: Area gadget (kind:'area') - clickable bevel box with
gadgetdown/gadgetup/mousemove events
- NEW: evt.x, evt.y mouse coordinates on gadget events
- NEW: Debugger 'p' prints objects/arrays recursively
- FIX: Scroller/slider gadgetup not firing (added
GA_RelVerify + GA_Immediate tags)
- FIX: Regex escape sequences (\n, \r, \t, \f, \v, \0)
- FIX: aminet_browser.js eating 'r' chars from readme
- FIX: VM hex/octal/binary clamped at 0x7FFFFFFF
- FIX: VM OP_GET_PROP for Number.prototype methods
- FIX: toString(radix) for values >= 2^31 on soft-float
- FIX: double-to-ULONG in FFI for values > 0x7FFFFFFF
0.18.1 (2026-04-14)
- FIX: Scroller drag ID - switching between scrollers no
longer emits stale IDs
- FIX: Non-GZZ window gadget coords aligned with
gfx.innerSize()
- FIX: MOUSEMOVE identifies gadget from pointer, not from
stale active_gad_id
0.18.0 (2026-04-13)
- NEW: Arrow keys, F1-F10, Help key events via IDCMP_RAWKEY
(evt.key = "ArrowUp"/"ArrowDown"/"ArrowLeft"/"ArrowRight"/
"F1".."F10"/"Help") -- both gui and intuition
- NEW: Vertical scroller orientation (vertical:true)
- NEW: gui.set() scroller accepts object
{top, total, visible}
- FIX: Duplicate key events prevented (RAWKEY filtered for
VANILLAKEY codes)
- FIX: Modifier keys filtered from RAWKEY
- Example: gui_keyboard_and_scrolls.js
0.17.0 (2026-04-12)
- NEW: gui.gfx sub-module -- drawing primitives on GadTools
windows: setColor, setBColor, setDrawMode, moveTo, lineTo,
drawLine, drawRect, fillRect, drawCircle, fillCircle,
drawEllipse, drawText, setPixel, getPixel, clear,
innerSize, setFont, waitTOF
- FIX: Intuition openWindow uses WA_InnerWidth/Height so
dimensions match drawable area (was outer)
- FIX: Intuition gfx.clear uses EraseRect (GZZ-safe) instead
of SetRast
- FIX: Intuition resize events update win.width/height
- FIX: Intuition keypress includes 'key' property
- Examples: gui_drawing.js, gui_gfx_clock.js
Credits
-------
Juen/Project R3D+Appendix+Nah-Kolor
|