All work

Project

Terminal Engine

A console engine. Write for the terminal, run in the browser too.

A virtual terminal built around one class, BasicConsole. Output, input events, cursor control, clearing and column measurement live behind its API. An app wires them in once; swapping the host hooks moves it from the Node terminal to xterm.js in the browser, or into tests, without rewriting its render code. Two apps run on it today: Console Adventure and RTSim.

Public demos · no account

Two programs, one console interface

  • Console Adventure

    Fantasy RPG in the terminal: wander the Magical Lands, fight enemies, find gear. Object-oriented, Node only.

  • RTSim

    Discrete-time preemptive scheduler simulator for one or more CPUs: EDF, FCFS, HRRN, Priority, Round Robin, SJF and PriorityRR, drawn at up to 60 fps.

Why separate the console from its host

Both apps were written for the Node terminal, and each drew its screens through its own console helpers. RTSim's README already said its small render engine needed to be decoupled.

Pulling that layer out into one engine, with the host plugged in underneath, is what lets the same render code run in a real terminal and in the browser, and gives the next console app somewhere to start.

What the adapters make possible

  • Render once, any host

    Render code asks one object to write, clear, show the cursor or measure the screen. It never knows whether that is Node, xterm.js or a test.

  • Layout that understands colour

    Center, wrap, crop and slice text safely even when it carries ANSI colour codes, so a coloured frame still lines up.

  • Keys the app can trust

    Browser key events become stable strings such as enter, space, esc and the arrows before the app sees them.

  • Resize, then redraw

    The host terminal is fitted first, then the app redraws against the new size. The console stays the one source of width and height.

Where the host boundary lives

  • Render code never knows the host

    The only dependency of a screen is the console class. Node, xterm.js and tests each supply an adapter for the same methods. The cost is one adapter per host, and a small API that every app has to stay within.

  • Input is normalized at the edge

    Input sits next to the console class, not inside it. The browser host turns key events into stable app-level strings, with modifiers passed alongside when needed, so game and simulator logic never parse DOM events.

  • Deterministic builds over live submodules

    Production does not fetch submodules, follow floating branches or depend on the host's git checkout. It builds from committed snapshots of each app. The cost: the snapshots are refreshed by hand.

Apps, console and adapters

Apps render through the engine's one console class, BasicConsole. Host adapters bind that class to the Node terminal, to xterm.js in the browser, or to tests.Terminal EnginerenderrenderConsole AdventureRPGRTSimscheduler simulatorBasicConsoleone console APINode adapterprocess.stdoutxterm.js adapterbrowserTest adaptertests
  • The site is a React + Vite host for the engine's xterm.js side: / lists the apps, /console-adventure and /rtsim run them.
  • Each app keeps its own repository, pinned in the catalog as a git submodule.
  • Production builds from committed source snapshots, so Stackport never needs a recursive clone.

Project context

Team
Solo, design to deploy
Period
2024-09 – now
Status
Live at terminal.mattediworks.com
Stack
JavaScript, Node.js, React, xterm.js

What is still coupled

  • The engine lives inside the apps' sources; it is not an extracted package yet.
  • Source snapshots for production are refreshed by hand.
  • The repository is due a review: its default branch is behind the production branch.