Project portfolio

Open-source projects by Henry Romp.

RompMusic

Libre and gratis music streaming — your music, your server, your freedom. RompMusic is a complete self-hosted system: libre (GPL-3.0) and gratis (no subscription). You own your music and data; no tracking, no proprietary services.

Why RompMusic?

  • Libre software — GPL-3.0: use, study, modify, share.
  • Gratis — No paywalls or proprietary lock-in.
  • Self-hosted — Library stays on your server; full privacy.
  • Cross-platform — Android, iOS, and web.
  • Gapless playback — Albums without gaps between tracks.
  • No tracking — Zero telemetry or analytics.

Components

rompmusic-serverStreaming, library management, REST API, JWT auth, HTTP range requests, admin panel, Mutagen metadata, optional beets integration.
rompmusic-clientExpo / React Native: Android, iOS, web; gapless playback, playlists, play history, paginated library; JWT login.
rompmusic-websiteDocumentation and marketing at rompmusic.com.

Server (quick start)

python -m venv venv && source venv/bin/activate
pip install -e .
cp .env.example .env
uvicorn rompmusic_server.main:app --reload

Notable env: MUSIC_PATH (library root), AUTO_SCAN_INTERVAL_HOURS, BEETS_AUTO_INTERVAL_HOURS. Dashboard scans use a background DB session so they continue if the tab closes.

Client (Android playback)

Background playback uses react-native-track-player: native service owns the queue; JS dispatches play/pause/seek; events sync back to the store. Patch included for Kotlin nullability with Expo SDK 54.

Docker (umbrella)

git clone --recursive https://github.com/151henry151/rompmusic.git
cd rompmusic
cp .env.example .env
# Edit DB_PASSWORD, JWT_SECRET, …
docker compose up -d

Docs: Install, Configuration, Self-hosting.


Dark Chess

Multiplayer web chess where each player’s board is partially hidden: you always see your own pieces, but you only discover the rest along paths your pieces have traveled. Standard chess rules; server is authoritative. Credits: Charlie Bishop conceived Dark Chess; this implementation was built in collaboration with him.

Features

  • Fog-of-war board; lobby; vs computer (bot uses same fog rules); challenges; random colors.
  • Fischer time controls (minutes+increment); server-side clocks; captured-piece counts.
  • Post-game review: fog lifts; replay full position history.
  • Full chess via chess.js: check, mate, stalemate, castling, en passant, promotion, draws, resign.
  • Disconnect handling; return to lobby.

How visibility works

The server keeps the true position and per-player path memory; each client gets a masked view. Opponent moves do not reveal the board for you.

  1. Your pieces — always visible.
  2. Your traveled squares — cumulative paths: pawns (each step); knights (chosen L-route, every square on the L); sliders (full ray); kings (start/end; castling paths); en passant capture square on path.
  3. Captures against you — capture square revealed afterward.
  4. Visible squares — empty reads empty; occupied shows that piece if visible.
  5. Probes — illegal pawn push into block, or fake diagonal capture to empty (non–en passant): lose turn without moving; destination stays explored.
  6. Sliders — enemy on ray: capture first blocking piece; (friendly-block behavior per engine rules).
  7. Into check — illegal destinations still highlight; attempt fails without passing turn; hypothetical attackers flash red.

Server sends dark-chess targets after piece select; every move is validated.

Architecture

ExpressStatic from public/.
Socket.IOLobby, challenges, rooms, moves.
chess.jsAuthoritative position on server.
server/dark.jsMasked boards and path visibility.
darkchess/
├── server/index.js    # HTTP + Socket.IO
├── server/dark.js     # Fog / masking
├── public/            # index.html, app.js, style.css
└── package.json

Run & deploy

Node 18+; ESM. npm install, npm starthttp://localhost:3333 (override PORT). State is in memory—one process or sticky sessions. Behind nginx, proxy /darkchess/ and /darkchess/socket.io/ (v1.3.1+ uses relative assets and socket path). GPL-3.0+.


Gödel, Escher, Bach companion

An interactive, chapter-by-chapter companion to Gödel, Escher, Bach: An Eternal Golden Braid by Douglas R. Hofstadter. Fan-made educational project; book text is Hofstadter’s (PDFs split per chapter).

What this is

  • Landing — Introduction + Chapters I–XX with short descriptions.
  • Per chapter — Reading level switcher: ELI5, ELI10, ELI20, and Full Text (embedded chapter PDF only).
  • Layout — Left: explanations or PDF iframe; right: unique interactive companion (MIU system, pq-system checker, figure–ground, etc.).
  • Design — Vanilla HTML/CSS/JS; shared/style.css, shared/nav.js; fonts EB Garamond + Inter.

Structure

  • index.html — chapter grid; intro/, chapter-01/chapter-20/.
  • pdfs/intro.pdf, chapter-01.pdf … (one scroll range per iframe).
  • scripts/split_geb_pdf.py — regenerate PDFs from full book (Python 3, pypdf).
  • PLAN.md — chapter plan and companion concepts.

Build: none—serve static files from /geb/. Each chapter embeds only its PDF segment so viewers cannot scroll into adjacent chapters.


Doodlr

Collaborative, zoomable pixel-art canvas with a 6-level hierarchical grid (729×729 pixels total). Draw pixels, zoom 1–6 in a 3×3 section grid per level, consistent alignment across zoom. Production: Expo web at /doodlr/app/, API proxied at /doodlr/api/.

Tech stack

Backend: FastAPI, Uvicorn, SQLAlchemy, SQLite. Frontend: Expo, React Native, React Native Web. E2E: Playwright.

API (excerpt)

  • GET /health, GET /colors
  • GET /level/{level} — canvas data; levels 2–6 need section_x, section_y
  • POST /paint{ x, y, color }
  • POST /zoom — validate { level, section_x, section_y }

Getting started

bash ./start-backend.sh    # http://localhost:8000
bash ./start-frontend.sh   # Expo; press w for web

Structure: backend/, frontend/, optional shared/. Version in VERSION; license GPL-3.0-or-later.


Listen

Android app that continuously records ambient audio in the background and maintains a configurable rolling buffer—review recent conversations or sounds. GPL-3.0; all storage local; no cloud upload for core recording.

Key features

  • Continuous background recording; configurable segment length (15s–30 min).
  • Auto Music Mode — silence-based splits targeting ~5 min segments.
  • Retention window; rotating buffer deletes oldest segments.
  • Quality presets: Low 16 kbps, Medium 32 kbps, High 128 kbps.
  • Export; WorkManager for rotation; Room DB for segment metadata.

How it works

ListenForegroundService orchestrates capture; MediaRecorder for encoding; segments as files; SegmentManagerService for rotation. Flow: record → split → prune when over retention.

Privacy

No network permission required for core recording; microphone permission explicit; optional encryption; one-tap delete.


Archive to Video

Turn archive.org audio collections into YouTube videos: paste a details URL, preview tracks, sign in with YouTube, then download, mux still artwork + audio (e.g. 1080p H.264 / AAC), upload, and create a playlist. GPL-3.0. Web UI at /archive-to-video/app/; CLI for headless use.

Features

  • URL input; preview; auto-generated videos; YouTube OAuth; playlist creation.
  • Metadata from archive pages (artist, venue, date, lineage).
  • Resume: skip downloads/uploads already done.

Flow

  1. Paste archive.org details URL.
  2. Preview tracks and durations.
  3. Authenticate with YouTube.
  4. Process: download audio → ffmpeg → upload → playlist.

Stack

FastAPI/uvicorn (systemd on hromp), ffmpeg, Python 3. CLI: python upload.py <archive.org URL> after install—see repo README.


Anthill

Godot 4.2+ (GDScript) grain-scale voxel simulation of an ant colony inspired by Lasius niger: recruitment pheromone trails, cuticular hydrocarbon footprints, alarm-like signaling, nest excavation in granular substrate, brood and workers—not a casual game but a research-oriented model with HUD and validation export.

Model contents

  • Substrate & nest — heightmap-style world; sand grains; gravity; nest galleries.
  • Pheromone & CHC fields — recruitment trail (deposit, diffusion, evaporation), footprint traffic field, nest cue, stress alarm.
  • Life cycle — queen founding, brood, nanitics/workers, trophallaxis, task allocation (forage, dig, care).
  • Workers — scout/recruit tropotaxis, zigzag sampling, food memory, trail marking.
  • Instrumentation — CSV logs, tracing; see docs/reference/technical_specification.txt.

Run

Prebuilt Windows EXE, Linux AppImage, and raw ELF on GitHub Releases (mirrored under hromp /downloads/). Or open game/anthill in Godot 4.2+.


invoice-gen

Flask app for small businesses and freelancers: manage companies and clients, add labor and itemized lines, upload logos, apply sales tax, and generate invoices (HTML preview + Excel download). Draft autosave syncs to the server and browser storage. Production on hromp: Docker + Gunicorn behind nginx at /invoice/ via SCRIPT_NAME=/invoice.

Structure

invoice_gen/app.py — main Flask app; templates/ — Jinja2 UI; static/ — CSS/JS and uploaded logos; requirements.txt; Alembic migrations under migrations/. Pretty invoice template: templates/invoice_pretty.html.

Run (dev / Docker)

python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
flask run

Production-style: docker-compose up --build (Gunicorn). Dev with hot reload: docker-compose -f docker-compose.dev.yml or ./switch-env.sh dev / prod. Key env: FLASK_ENV, SECRET_KEY, DATABASE_URL, SCRIPT_NAME=/invoice when mounted under a prefix.

Extras

  • Optional Google Places address autocomplete — API key in credentials.ini (see README).
  • REST-style JSON endpoints for clients, companies, invoice number checks, sales tax CRUD, session selections.
  • Test harness script populates sample businesses, clients, and invoices for manual QA.

Weather dashboard

Real-time GOES-19 satellite animations, NWS current conditions and 7-day forecast, forecast maps, and severe weather alerts—vanilla HTML/CSS/JS (no framework), dark theme, grid layout. MIT.

Satellite bands

CONUS GeoColor, snow/ice, day/night cloud combo, full-disk GeoColor, air mass RGB, sandwich RGB, ozone, water vapor (lower/mid/upper), dust RGB, and more—24-frame loops with hover-to-pause and click-through to NOAA.

Location & alerts

ZIP-based location with coordinate lookup (Zippopotam.us); dismissible, color-coded alerts. Temperature and precipitation forecast map links update with the selected location.

Technical

Custom frame-based animation; fetch() to NWS and STAR endpoints; CSS Grid/Flexbox. Files: index.html, styles.css, links.txt. Serve with any static host (python3 -m http.server for local dev).


Word roots

Browser tools around Donald J. Borror’s Dictionary of Word Roots and Combining Forms (1960): searchable index extracted from the bundled PDF, a species-name / compound builder from plain English, and an in-app link to the full PDF. Vite + React + TypeScript; runtime loads only dictionary.json—no API. GPL-3.0.

Features

  • Search + A–Z browse — substring search (up to 200 hits) or letter row in book column order; optional linter scripts flag OCR ordering quirks.
  • Name builder — drop stopwords, match glosses, chain stems with Greek/Latin linking rules; outputs draft epithet—still needs real taxonomy checks.
  • Entry cards — expand (G)/(L) tags; collapsed introduction panel with Borror’s preface.

Build & data

cd web && npm install && npm run dev
# Production: VITE_BASE=/word_roots/ for https://hromp.com/word_roots/
python3 scripts/extract_dictionary.py   # regenerate dictionary.json from PDF

Requires pdftotext (Poppler) to regenerate JSON from the PDF.


Eternal Weave

Canvas puzzle game in a “block universe” framing: you shape one 4D worldtube—3D space (x,y,z) across discrete time slices. The UI tracks level, score, chronons, lockline, anchors, and toggles A-series / B-series observation modes plus frame tilt and orbit camera. Static game.js + styles.css under /time/.

Gameplay loop

Shape future slices before the lockline passes them; follow anchor schedule and resolve entries on the paradox ledger. Lore text per level explains the temporal metaphor (“every slice exists at once,” observation frontier freezing the past).

Controls (excerpt)

  • F/R or arrows — move selected time slice; WASD / QE — move y / x / z at that slice.
  • Z/X — frame tilt; Space — A/B view; Shift — camera preset; mouse drag orbit, wheel zoom; Backspace reset; Enter advance after stabilization.
  • Mobile: on-screen slice and move grids.

SpeedRead

RSVP reader: paste text or upload PDF, TXT, MD, HTML, XML, JSON, CSV. pdf.js (bundled worker) extracts PDF text in the browser—no document upload to a backend. Adjustable WPM, font size, optional center-letter highlight, play/pause and word skip. DM Sans + JetBrains Mono.

Architecture

Vanilla JS modules: timer, tokenizer, display; CSS variables for typography. Keyboard: Space play/pause, arrows for prev/next word. Privacy: processing stays in the tab; hromp only serves static assets.


Day / night map

Real-time day/night terminator on a world map: D3 v7 + TopoJSON v3, 110m country data, current time display, and short educational text on axial tilt and seasons. Dependencies load from CDN—open index.html locally or visit /mercator/. MIT.

Files

index.html, style.css, map.js, data/countries-110m.json. The map redraws the terminator continuously for the viewer’s “now.”


Solar System calculator

Single-file interactive tool: enter a Sun diameter and get all planet diameters and orbital distances to the same scale, plus a pairwise distance calculator between solar system bodies. No npm—one index.html, MIT.

Deploy

Serve the file from any static root; README includes an example nginx location block for a subpath.


Travel time calculator

Trip duration from a trapezoidal velocity profile: total distance, max speed, and separate acceleration and deceleration durations. Supports metric (km, km/h) and imperial (mi, mph). Static index.html; no dependencies.

Model

Accel phase — user-specified ramp time. Cruise — remaining distance at max speed. Decel phase — user-specified ramp down. Total time is the sum—useful for rough ETA when acceleration matters (not constant-speed-only).


Birthday in π

Pick a calendar date and search for MMDDYYYY as a digit string in the first ten million decimal digits of π. Digits load in chunks to keep memory low; search runs entirely in the browser. Includes short educational copy on irrational/transcendental π and normality.

Data & credits

Digit file from calculat.io. Conceptually inspired by the classic Am I in Pi? toy.


Rubik’s cube

Single-file Three.js (r128) + OrbitControls 3D cube; cubejs Kociemba two-phase solver vendored under lib/ with a same-origin worker so Firefox can run the solver. Face turns via keyboard (US, Shift for prime) or drag on a face; scramble, show scramble, animated solve, reset. Single static index.html workflow; live at /charlie/.

View options

HUD Top menu picks which WCA face is “up” (persisted in localStorage); camera distance rotates with the choice so the perspective stays corner-like. Notation and solver stay standard (U = white on +Y in the model).


On Denoting

Interactive companion to Bertrand Russell’s 1905 Mind paper “On Denoting.” Left pane: full article typeset for long reading (Libre Baskerville) with a reading level switcher—Full article vs ELI5 / ELI10 / ELI20 summaries that walk through definite descriptions, Meinong/Frege objections, and the three puzzles (Waverley, king of France, difference between A and B). Right pane: companion iframe (split layout collapses to stack on narrow screens).

Purpose

Makes Russell’s analysis of denoting phrases—existence/uniqueness conditions, primary vs secondary occurrence, knowledge by description—readable without a PDF reader, while optional ELI layers scaffold the logic for students.


IPA reader

Understanding the IPA — static explainer with embedded chart image, prose on consonants (place, manner, voicing) and vowels (height, backness, rounding), a link to UCLA’s interactive chart with audio (ucla_ipa.html), Ken Stevens x-ray video, and a long article on English spelling vs phonology. Optional Flask backend proxied at /ipa/api/ for IPA lookup from the inline form. Submodule: ipa-chart/ → served at /ipa/.

Credit

Teaching copy credited to Amy Reynolds (UNC-Chapel Hill); English spelling article credited to Sarada Mauck (Iowa State) in-page.


Too Hot

Project hub whose production site is its2hot.org—developed in the too-hot GitHub repo and deployed off the main hromp static tree. The hromp /too-hot/ page is a landing stub only: it documents that marketing and app assets ship from Git + CI to the external host.

Architecture (typical)

Exact stack lives in the repo; expect versioned static or server-rendered pages with deploy to the its2hot server. hromp keeps a pointer for consistent navigation with other portfolio entries.