The art board of the BBS network: classic ANSI art from the 16colo.rs archive, a shared 40×25 canvas that every caller paints on at the same time, and the Wall of the works saved from it.
Live at
teletypegames.org:2332, in the browser on bbsweb, and through TTG Gate BBS
A Ruby app on the rubbs gem, like the Teletype BBS, Scriptorium BBS and Commodex BBS.
It has two faces on one port:
A call starts with rubbs' BBS::Terminal question: RETURN takes the guess, 1 picks the modern face, 2 the C64.
16colo.rs. The board browses the 16colo.rs archive through its public API: a year, a pack, a file. It lists the text-art files (.ans, .asc, .diz, .nfo, .txt, .ice, and any file whose SAUCE says ASCII, ANSI or ANSiMation) with their title, artist, group and 16colo.rs address. Nothing is mirrored: a file is fetched when someone opens it (at most 512 KB) and kept in a 100 MB disk cache; the listings stay an hour in memory.
The board renders the art itself: it reads the SAUCE record, CP437, the ANSI.SYS escapes and iCE colours, wraps at the SAUCE width, and stops at 1000 rows. On a modern terminal the piece scrolls on the whole screen. On a C64 it comes in pages of 40 columns: the CGA colours go to the nearest C64 colour, a coloured background becomes reversed video, and R or CRSR right steps across a wider piece.
The Wall holds the works saved from the canvas, newest first, with title, handle and date, drawn natively on both faces.
One shared 40×25 canvas, the size of the C64 screen, so both faces show it cell for cell. Everyone on it sees the others' strokes within a few seconds. A cell holds one character in one of the 16 C64 colours, as ink on black or reversed; that is what a C64 cell can show, so the picture looks the same everywhere. The characters are the ones both a Unicode terminal and PETSCII have: letters, digits, most signs, box lines, blocks and the half blocks.
On a modern terminal:
| Key | Action |
|---|---|
| F3 | Open the canvas |
| Arrows, typing | Move, paint the character |
| F7, F8 | Previous / next ink (or click a swatch) |
| F9 or Ctrl-R | Reverse on and off |
| Insert or Ctrl-G | Glyph mode: letters paint the brush glyphs |
| Mouse | A click places the cursor, a drag or a right click paints |
| Ctrl-Z or Ctrl-U | Undo your own last cells |
| Ctrl-S | Save to the Wall (Alt-S in the form) |
| F2 | 16colo.rs by year |
| F4 | The Wall |
| Alt-X | Log off |
On a Commodore 64: CRSR moves, typing paints, DEL rubs out, F3/F4 change the ink, F5 turns reverse on and off, F6 switches glyph mode (the letters are laid out like the keyboard: Q W E / A S D / Z X C are the box corners, tees and the cross, G is a full block, T and B the top and bottom halves). RETURN, RUN/STOP or ← opens the command row: ink, reverse, glyphs, save, help, quit. F1 and F7 are left alone, UltimateTerm keeps them. The board sends only the cells that changed, a couple of bytes each, so the canvas stays quick at 1200 baud.
Limits: 20 cells a second per call, one Wall save every five minutes per call, a title of 32 and a handle of 16 characters.
| Variable | Default | |
|---|---|---|
PIXELIA_BBS_DATA |
data |
the canvas, the Wall, the cache |
PIXELIA_BBS_API_URL |
https://api.16colo.rs |
the 16colo.rs API |
PIXELIA_BBS_SITE_URL |
https://16colo.rs |
the files and the links shown |
PIXELIA_BBS_USER_AGENT |
PixeliaBBS/1.0 (+https://teletypegames.org; …) |
sent to 16colo.rs |
PIXELIA_BBS_API_TTL |
3600 |
seconds a listing is kept |
PIXELIA_BBS_MAX_FILE_KB |
512 |
the largest art file fetched |
PIXELIA_BBS_CACHE_MB |
100 |
the art cache's size |
PIXELIA_BBS_MAX_ROWS |
1000 |
rows rendered of a piece |
PIXELIA_BBS_PAINT_RATE |
20 |
cells a second per call |
PIXELIA_BBS_WALL_COOLDOWN |
300 |
seconds between two saves of a call |
The canvas is data/canvas.json (written every two seconds), the Wall one JSON file a work in data/works/. The admins moderate on the server:
docker exec pixelia-bbs bin/works list # the Wall
docker exec pixelia-bbs bin/works show ID
docker exec pixelia-bbs bin/works remove ID
docker exec pixelia-bbs bin/canvas clear # archives the canvas to data/archive/, then blanks it
The board has no secrets.
A push to master deploys it from Woodpecker (RuboCop, compose check, ci-deploy pixelia-bbs); the container is pixelia-bbs, in /srv/stacks/pixelia-bbs, and data/ stays between deploys. The entrypoint updates rubbs on start.
The code follows the house layering: lib/domain (the colour, glyph, canvas, work, SAUCE and art models; the 16colo.rs, art-cache, canvas and work repositories; AnsiRenderService, GalleryService, CanvasService, WallService; the composition root PixeliaBBS) knows nothing of telnet, and lib/ui holds UI::Modern with its canvas widget and viewer, and UI::Petscii with a screen copy that sends only the differences.