Skip to content

Projects

Selected work

Projects

Things I have built. The desktop view shows them as an interactive solar system — here is the readable list.

  • HRM

    live

    Full-stack HR management system

    Production-ready HR system built to portfolio standards. Two databases (PostgreSQL for structured data, MongoDB for an immutable, hash-chained audit log), 38 granular permissions with per-user overrides, TOTP 2FA, server-side rate limiting, OpenTelemetry tracing, 18 languages, and real-time activity notifications over SSE (with polling fallback).

    • 2906+ tests
    • 92.2% coverage
    • PostgreSQL + MongoDB

    Integrations

    • Google OAuth
    • GitHub OAuth
    • Sentry
    • OpenTelemetry
    • Next.js
    • React
    • TypeScript
    • PostgreSQL
    • MongoDB
    • Prisma
    • MUI
    • Jest
    • Playwright
    • Docker
    • GitHub Actions
    • Zod
    • NextAuth
    • next-intl
    • ReactFlow
    • Pino
    • pg-boss
  • Platform

    live

    Community platform built on HRM

    Live community platform serving a real WoW guild at vuohiliitto.com. Turborepo monorepo with HRM as a git submodule. Multi-tenant, with WoW-themed gamification (XP, levels, achievements, quests), tabbed chat with whispers and slash commands, a Mythic+ team tracker via the Raider.IO API, and a guided tour for new members.

    • Real users
    • Multi-tenant
    • 1388+ tests

    Integrations

    • Raider.IO API
    • Google OAuth
    • GitHub OAuth
    • GitHub API
    • Turborepo
    • Next.js
    • React
    • TypeScript
    • PostgreSQL
    • Prisma
    • NextAuth
    • MUI
    • Playwright
    • Jest
    • next-intl
  • Portfolio

    wip

    This site

    The site you are looking at. Fully static, built with Astro, Three.js and GSAP. A visual showcase of motion craft, intentionally separate from the production stack used in HRM and Platform.

    • Astro
    • Three.js
    • GSAP
    • TypeScript
    • Tailwind CSS
  • ReadLog

    live

    Track every book you've read

    Personal reading tracker. Searches Open Library and Google Books in parallel and deduplicates results, then lets you log books with format (paper / e-book / audiobook), a 0–5 star rating, and finish date. Public anonymous feed of recently logged books on the homepage.

    • 90 tests
    • Multi-source search

    Integrations

    • Open Library
    • Google Books
    • Google OAuth
    • Next.js
    • React
    • TypeScript
    • Prisma
    • PostgreSQL
    • NextAuth
    • MUI
    • Jest
  • ReadLog .NET

    live

    ReadLog, ported to ASP.NET Core

    An idiomatic ASP.NET Core port of ReadLog, running live and free on Azure App Service. The same reading-log app — search Open Library and Google Books, log books with format, finish date, and a 0–5 rating, then browse your library and a public "recently read" feed — re-expressed in .NET 8 Razor Pages, EF Core + SQLite, and ASP.NET Core Identity (local and Google sign-in). Containerized and shipped by a reviewer-gated GitHub Actions pipeline to GHCR, then on to Azure over OIDC; EF Core migrations apply on first run.

    • ASP.NET Core 8 port
    • Live on Azure App Service
    • EF Core · SQLite · OIDC deploy

    Integrations

    • Open Library
    • Google Books
    • Google OAuth
    • .NET 8
    • ASP.NET Core
    • Razor Pages
    • EF Core
    • SQLite
    • ASP.NET Identity
    • Bootstrap 5
    • xUnit
    • Docker
    • GitHub Actions
    • Azure App Service
  • AudiobookMaker

    wip

    PDF → audiobook

    Desktop app that turns PDF, EPUB, Word/DOCX, or plain text files into audiobooks; scanned PDFs are run through Tesseract OCR first. Four TTS engines: Edge-TTS (cloud, 30+ voices in 6 languages), Piper (offline, no GPU needed), Chatterbox with the "Grandmom" voice for voice cloning from a short reference clip, and VoxCPM2 for zero-shot voice cloning and voice design from text (NVIDIA GPU, developer setup only). The same Chatterbox engine voices the in-game story of Spacepotatis. English output quality is already strong; Finnish is harder to synthesize with available resources, so it gets a dedicated 19-pass text normalization pipeline that handles governor-word number inflection, abbreviation expansion, unit agreement, and loanword respelling — advancing with every release. Ships as a Windows installer with auto-updates and 3000+ tests.

    • Chatterbox voice cloning with the Grandmom voice
    • 19-pass Finnish text normalization, 3000+ tests
    • Voices the in-game story of Spacepotatis

    Integrations

    • Microsoft Edge-TTS
    • Python
    • PyMuPDF
    • ebooklib
    • ocrmypdf
    • Tesseract
    • num2words
    • edge-tts
    • Piper
    • Chatterbox
    • VoxCPM2
    • PyTorch
    • CustomTkinter
    • pydub
    • pygame
    • ffmpeg
    • PyInstaller
    • Inno Setup
    • GitHub Actions
  • Spacepotatis

    live

    Browser shooter — your potato vs the galaxy

    Live browser game where a potato in a shield bubble shoots bugs across a procedural galaxy. Boots like a vintage terminal, opens into a 3D solar system you drag and zoom, drops you into top-down vertical combat in the spirit of Tyrian 2000. Next.js 16 + React 19 wraps a Phaser 4 combat scene; Three.js + GSAP power the galaxy view and the camera transition into combat; PostgreSQL on Neon is talked to via Kysely (typed SQL builder, no ORM). All voice generated by AudiobookMaker; all music written in strudel-patterns. Ships a catalog of custom Claude Code skills under .claude/skills/ — version-controlled, audited, treated as production artifacts.

    • Next.js 16 + Phaser 4 + Three.js
    • ~1300 tests, CI on every push
    • Custom Claude Code skills as production artifacts

    Integrations

    • Google OAuth
    • Next.js
    • React
    • TypeScript
    • Phaser 4
    • Three.js
    • GSAP
    • PostgreSQL
    • Kysely
    • NextAuth
    • Tailwind CSS
  • Strudel Patterns

    live

    Algorithmic music in Strudel

    Live-coded electronic music written in Strudel — a JavaScript pattern engine, port of TidalCycles. Every track is a single composable expression: stacked synths, basslines, drum patterns, and effect chains. Composed through a structured AI workflow — natural-language direction → generation → listen → iterate, with decisions logged alongside the git history. Selected tracks score Spacepotatis (galaxy overworld, mission themes, story narration bed) and the mikkonumminen.dev landing page. Reusable component library, curated synth presets, session notes per iteration.

    • Live-coded in Strudel
    • AI-directed iteration, logged in git
    • Soundtrack to Spacepotatis and mikkonumminen.dev
    • Strudel
    • JavaScript
    • Web Audio API
    • Claude Code
  • claude-continue

    wip

    Keep Claude Code windows back-to-back

    A cross-platform Python tool — a CLI plus a one-button Tkinter GUI — that keeps Claude Code's 5-hour usage windows running back-to-back. It reads the active window's reset time from ccusage, waits for the rollover, then resumes paused sessions — broadcasting to iTerm2 on macOS, typing into tmux panes on macOS or Linux, or a headless run on Windows/WSL — and re-arms for the next window. Runs unattended via launchd (macOS) or Windows Task Scheduler. Built for long autonomous agent runs, and honest about the review debt that pattern creates.

    • Python · Tkinter GUI
    • macOS · Windows · WSL · Linux (tmux)
    • Unattended via launchd / Task Scheduler
    • Python
    • Tkinter
    • ccusage
    • PyInstaller
    • launchd
    • Task Scheduler
    • tmux
    • GitHub Actions
  • PasswordManager

    wip

    Zero-knowledge password manager in Rust

    Local-first password manager where a single Rust crate is the only place crypto exists — Argon2id key derivation and XChaCha20-Poly1305 authenticated encryption, compiled natively and to WebAssembly. Four clients share it: an offline-first CLI vault on SQLite, a sync server that stores ciphertext only, an in-browser WASM client, and a Chrome extension with autofill and save-on-login. Per-entry nonces and AEAD bound to entry id + timestamp block record swapping; keys are zeroized after use; the master password never leaves the client. Every security choice is recorded as an ADR against an explicit threat model, and CI guards secrets out of the repo while running format, clippy, the full workspace tests, and the wasm build.

    • One crypto crate, four clients
    • Argon2id + XChaCha20-Poly1305
    • Server stores ciphertext only

    Integrations

    • Google OAuth
    • Tailscale Funnel
    • Cloudflare Tunnel
    • Rust
    • WebAssembly
    • Tokio
    • SQLite
    • Argon2id
    • XChaCha20-Poly1305
    • wasm-bindgen
    • Chrome Extension (MV3)
    • JavaScript
    • GitHub Actions
  • claude-agents

    live

    Cost-routing subagents for Claude Code

    A small global set of Claude Code subagents that stops paying Opus prices for work a cheaper model does just as well. Twelve agents, each pinning both model tier and reasoning effort in its frontmatter: Haiku for read-only recon (scout, log-miner, scribe, dep-checker, tidy), Sonnet for spec-driven edits (mechanic, test-writer, locale-translator, doc-scribe, migrator, bisect), and an unpinned architect that inherits the session model for design work. Agents auto-detect each repo’s stack — test runner, linter, i18n layout — so one set covers JS, C#, and Python projects. Installs as a Claude Code plugin from a shared marketplace or via script. MIT-licensed.

    • 12 model-pinned agents
    • Decouples model tier from reasoning effort
    • Plugin or script install
    • Claude Code
    • Claude Haiku
    • Claude Sonnet
    • Claude Opus
    • Bash
    • Markdown
  • Feedback Intelligence

    live

    Grounded feedback analysis with a local LLM

    Feedback-intelligence engine that turns messy free-text feedback into situational signal without letting the LLM near the numbers: a deterministic, rule-coded alert layer computes every count and trend, and the LLM only structures input and synthesizes theme narratives — each claim cited back to feedback ids and dropped to a fallback if it fails validation. Domains are pluggable modules (first: Finnish hybrid grocery–hardware retail); a config flag swaps in another domain with zero core edits. .NET 8 over a local Poro 2 8B on Ollama — picked in a 30-round blind test — with a live demo on Azure Static Web Apps reaching the local GPU through a Tailscale Funnel at zero cloud-inference cost. GDPR-clean synthetic corpus, prompt-injection defense in depth, hermetic xUnit suite in CI.

    • Deterministic alerts — LLM never computes the numbers
    • Pluggable domains, zero core edits
    • Live demo on Azure, inference on a local GPU

    Integrations

    • Tailscale Funnel
    • .NET 8
    • C#
    • SQLite
    • Ollama
    • Poro 2 8B
    • Microsoft.Extensions.AI
    • xUnit
    • Docker
    • Node.js
    • Azure Static Web Apps
    • Azure Functions
    • GitHub Actions
    • CodeQL