A Pokémon Mystery Dungeon-style personality quiz that recommends your ideal Linux distro.
The user takes a short quiz of situational, emotional questions — never about Linux itself — and at the end discovers the distro that matches their personality. The flow, tone, and visual style are inspired by the personality quiz that opens the Pokémon Mystery Dungeon games.
🌐 distrosoul.jtravesi.workers.dev
- React 18 + Vite for the frontend (single-page app)
- Tailwind CSS for styling, with a custom PMD-inspired theme
- Framer Motion for screen transitions and the reveal sequence
- Web Audio API for the synthesised ambient theme and typewriter blip (no audio files)
- Pixelify Sans + Press Start 2P as typography (loaded from Google Fonts)
- nginx-alpine to serve the built static files when self-hosting
- Docker for both dev and prod environments
Requires Node.js 20+.
npm install
npm run devVisit http://localhost:5173.
docker compose -f compose.dev.yml upSame URL. Source is mounted as a volume, so changes hot-reload.
docker compose up -d --buildVisit http://localhost:8080. This builds the static site and serves it via nginx, exactly as it would run in self-hosted production.
You can change the host port with the HOST_PORT env var:
HOST_PORT=3000 docker compose up -d --buildThe quiz has three logical pieces: the question pool, the six natures, and the distro matrix.
There are 19 situational questions in the pool. At runtime, 8 are picked at random and presented in random order. None of them mention Linux — they ask about a mysterious box at your doorstep, picking a restaurant in a new city, what kind of gift you prefer, and so on.
Each question has exactly 4 options, and each option awards points to one to three of the six natures. Most options give one point; particularly characteristic ones give two.
The six personalities the quiz scores you against:
| Nature | What it captures |
|---|---|
| Restless | Needs to touch, break, rebuild. Enjoys the process more than the result. |
| Serene | Wants things to just work. Values their time and peace of mind. |
| Stoic | Prefers what's tested by time. Conservative, patient, faithful. |
| Dreamer | Cares about aesthetics and how things feel. Beauty equals function. |
| Cautious | Cares about privacy. Reads the fine print. Trusts slowly. |
| Curious | Learns and explores for the pleasure of it. Reads docs for fun. |
After the 8 questions, the nature with the highest total wins. Ties are broken randomly (PMD-style: there's no priority order between personalities).
A fixed ninth question — "When the road gets hard, what keeps you going?" — has four options that map to four paths:
forward— the drive to go furtherplan— knowing exactly where you're headedhelp— the people who walk beside youpace— knowing the road is yours
This question awards no nature points. Its job is to pick the specific distro within the winning nature's family.
Each nature × path combination maps to a concrete distro. The full mapping:
| Nature | forward |
plan |
help |
pace |
|---|---|---|---|---|
| Restless | Arch Linux | Gentoo | EndeavourOS | EndeavourOS |
| Serene | Pop!_OS | Manjaro | Ubuntu | Linux Mint |
| Stoic | Slackware | Debian | openSUSE Leap | MX Linux |
| Dreamer | Garuda Linux | Pop!_OS | Zorin OS | elementary OS |
| Cautious | Tails | Qubes OS | Parrot Home | Whonix |
| Curious | CachyOS | openSUSE Tumbleweed | Fedora | NixOS |
That's 23 cells covering 22 distinct distros — Pop!_OS appears in both Serene and Dreamer with two different reveal texts, reflecting its dual personality. The Restless × help cell has no dedicated distro: if the scoring lands there it falls back to EndeavourOS (Restless × pace) as the closest neighbour.
A coverage script verifies that every reveal text is reachable and reports the empirical distribution under random play:
node scripts/coverage.mjs # default: 10k random runs
node scripts/coverage.mjs --runs 50000 # bigger sampledistrosoul/
├── src/
│ ├── components/
│ │ ├── TextBox.jsx # PMD dialogue with typewriter + ▼
│ │ ├── Choices.jsx # Staggered option menu
│ │ ├── IntroScreen.jsx # Welcome + Readme link
│ │ ├── QuestionScreen.jsx # One quiz question + 4 options
│ │ ├── FinalQuestionScreen.jsx # The tie-break question
│ │ ├── RevealScreen.jsx # 3-stage reveal with cream flash
│ │ ├── AboutScreen.jsx # Readme screen
│ │ └── SoundToggle.jsx # ♪ on/off corner button
│ ├── data/
│ │ ├── questions.js # Pool of 19 questions
│ │ ├── finalQuestion.js # The tie-break question
│ │ └── reveals.js # 22 reveal texts
│ ├── lib/
│ │ ├── scoring.js # Nature winner + distro mapping
│ │ ├── selectQuestions.js # Fisher-Yates random subset
│ │ ├── audio.js # Shared AudioContext + blip
│ │ ├── musicTheme.js # Procedural ambient loop
│ │ └── SoundContext.jsx # Sound enable/disable + provider
│ ├── App.jsx # Top-level state machine
│ ├── main.jsx # React entry point
│ └── index.css # Tailwind + PMD styles + bg waves
├── scripts/
│ └── coverage.mjs # Reachability + distribution dev script
├── docker/
│ └── nginx.conf # Hardened nginx config (CSP, headers)
├── Dockerfile # Production (multi-stage, nginx-alpine)
├── Dockerfile.dev # Development (Node + Vite)
├── docker-compose.yml # Production compose
├── compose.dev.yml # Development compose
└── ...
Released under the MIT License — feel free to fork, modify, and share. Attribution appreciated but not required.