An authentic 1990s BBS (Bulletin Board System) dial-up experience in your browser. Features character-by-character ANSI art streaming at simulated modem speeds with automatic rotation through 731 classic BBS welcome screens.
Live Demo: bbs.nitwit.se
- 731 ANSI Art Files - Classic BBS welcome screens from the 1990s
- Authentic Modem Simulation - ATZ, ATDT dial sequences, and NO CARRIER disconnect
- Variable Baud Rates - 2400, 9600, or 28800 baud based on file size
- IBM EGA Graphics - Authentic DOS font and 16-color palette
- CRT Effects - Scanline overlay for authentic terminal feel
- Lazy Loading - Fast initial load (~60KB), on-demand chunk loading
- Auto-Rotation - Automatically connects to random BBS after each session
Modern (2025):
- ES6+ modules with async/await
- Vite 5 build system
- @xterm/xterm 5.5.0 terminal emulator
- Lazy-loaded JSON chunks
- Zero dependencies (no jQuery)
Preserved Authenticity:
- CP437 (DOS) character encoding
- IBM EGA color palette
- Original timing and baud rate simulation
- Character-by-character rendering
bbsss/
├── src/ # Source code
│ ├── main.js # Application entry point
│ ├── terminal/ # Terminal configuration & CP437 mapping
│ ├── modem/ # Modem simulation sequences
│ ├── renderer/ # ANSI rendering & timing
│ ├── ui/ # Status bar & UI updates
│ └── data/ # ANSI data loader
├── public/ # Static assets
│ ├── data/ # JSON chunks (generated)
│ ├── fonts/ # IBM EGA fonts
│ └── scanlines.png # CRT effect overlay
├── ansi/ # Source ANSI files (731 files)
├── dist/ # Production build output
├── generate.py # Generate JSON chunks from ANSI files
├── package.json
└── vite.config.js
- Node.js 18+
- Python 3 (for regenerating data)
# Install dependencies
npm install
# Start dev server (http://localhost:3000)
npm run dev
# Build for production
npm run build
# Preview production build (http://localhost:4173)
npm run previewIf you add/remove ANSI files in the ansi/ directory:
python3 generate.pyThis creates:
public/data/index.json- File index (57KB)public/data/chunk-*.json- Data chunks (8 files, ~600KB each)
The dist/ directory contains everything needed:
npm run build
# Upload contents of dist/ to your web serverWhat gets deployed:
index.html- Entry pointassets/- Bundled JS (~296KB) and CSS (~4KB)data/- JSON chunks (8 files, ~5MB total)fonts/- IBM EGA fontsscanlines.png- CRT overlay
npm run build
# Push dist/ contents to gh-pages branchThe screen saver can load the website directly from bbs.nitwit.se:
- Configure your web-based screen saver to point to
https://bbs.nitwit.se - The application will auto-rotate through BBS systems
- Works offline if you deploy
dist/locally
- Initial Load: 75KB gzipped (JS + CSS)
- First ANSI: +57KB (index.json) + ~600KB (one chunk)
- Total Assets: ~5.4MB (all chunks + fonts)
Before modernization:
- 5.5MB embedded ANSI data loaded immediately
- Slow initial page load
After modernization:
- ~60KB initial load (98% reduction!)
- Chunks loaded on-demand
- Sub-second initial render
// Load index once (57KB)
const index = await loadIndex();
// Select random file
const file = index.files[randomIndex];
// Load only needed chunk (~600KB)
const chunk = await loadChunk(file.chunk);async function BBSLoop() {
await delay(200 * timeScale);
await writePreamble(terminal, baudRate, timeScale);
await writeWithDelay(terminal, content, 24, timeScale);
await writePostamble(terminal, timeScale);
window.location.reload();
}- terminal/ - Configuration, CP437 encoding
- modem/ - Preamble/postamble sequences
- renderer/ - Character streaming, timing
- ui/ - Status bar, clock
- data/ - ANSI loading with caching
Edit src/terminal/config.js to customize:
export const BAUD_RATES = {
SLOW: 2400, // For files < 3000 bytes
MEDIUM: 9600, // For files 3000-10000 bytes
FAST: 28800 // For files > 10000 bytes
};
export const FILE_SIZE_THRESHOLDS = {
SMALL: 3000,
LARGE: 10000
};- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
Requires ES6 modules and async/await support.
- ANSI art from textfiles.com
- Terminal emulation by xterm.js
- IBM EGA fonts recreated for web
ISC
This codebase was modernized from a 2010s-era hack into clean, maintainable 2025 code:
Phase 1: Vite + ES6 modules Phase 2: Removed jQuery, extracted modules Phase 3: Converted callbacks to async/await Phase 4: Lazy-loaded JSON chunks (98% size reduction) Phase 5: Upgraded to xterm.js v5.5.0 Phase 6: Production build & testing
See git history for the complete transformation!