Open-source project
Producdevity/EmuReady avatar
Producdevity/EmuReady

EmuReady, a community database of how games run on emulators

A community-driven platform for tracking emulation compatibility across different devices and emulators.

554 stars22 forksTypeScriptGPL-3.0

At a glance

What is it?
EmuReady is a Next.js site where people report how a specific game runs on a specific emulator, handheld or PC configuration, and where the reports are filtered by hardware, system, emulator and performance. The data is community supplied, so the interesting parts are the moderation model, the reproducibility fields and the self-hosting path.
Who is it for?
EmuReady suits someone who wants a filtered answer to whether a game runs on a device they own, and a project team that wants to host its own compatibility database with its own moderation rules. It does not suit a project that needs authoritative compatibility data, because every report is a user's account of their own setup.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 12 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A compatibility catalogue built from other people's testing

EmuReady is described as a community-driven platform for tracking emulation compatibility across different devices and emulators, and the README is more precise: it tracks how games run across emulators, handhelds, mobile hardware and PC configurations, where users share compatibility reports, compare setups, discuss results and help keep the catalogue useful through voting and moderation.

The subject matter is worth naming plainly, because the wording is careful. This is a database of how software runs under emulation, not a collection of emulators and not a place to obtain the software in question. A user reports that a game runs at a certain frame rate on a handheld with a particular emulator version and a particular set of settings, and other users vote, comment and moderate. The catalogue is the accumulated result.

That structure explains the design decisions visible in the feature list.

You can browse compatibility reports by game, platform, hardware, emulator and performance, and you can submit reports for handheld and mobile devices or PC hardware. You can compare CPU, GPU, device, system, emulator and performance filters. And you can add emulator-specific settings and notes so results are reproducible.

That last item is the one that separates a useful database from a useless one, and it is the hardest part to get right in a community project. A report saying the game runs well on a device is close to worthless, because the outcome depends on the emulator version, the graphics driver, the configuration of the emulator's settings, and which API the game is using. A report that includes the settings and notes is a recipe. The site's own framing, that results should be reproducible, is the requirement that turns a list of anecdotes into a reference.

The moderation machinery is the other half. You can use comments, votes, reports and moderation tools to keep the data useful, and there are admin workflows for maintaining game, hardware, emulator, custom field and approval data. That last category, approval, suggests that some submissions are held for review rather than published immediately, which is the normal design for a database where a bad row is a bad answer for a user who trusts the site.

The site runs at emuready.com, the primary language is TypeScript, the licence is GPL-3.0 with the manifest saying GPL-3.0-or-later, and the default branch is `staging`, which tells you something about the release workflow. The last push to the repository was on 2026-10-11.

The stack, and why the choices are conventional

The tech stack is one line in the README and there is nothing surprising in it.

EmuReady is a Next.js application written in TypeScript. It uses PostgreSQL with Prisma, tRPC for application APIs, Clerk for authentication, Tailwind CSS for styling, Vitest for unit tests and Playwright for browser tests.

Each of those choices has a reason that fits this project specifically, and the two that are worth explaining are Prisma and tRPC.

Prisma with PostgreSQL is the data layer, and the schema is where this project actually lives. A compatibility database is a relation between games, platforms, hardware, emulator configurations and performance observations, and the filtering the site offers is filtering over those relations. Prisma gives typed access to that schema, generates the client from it, and the migration tooling is what keeps the schema and the deployed database in step.

tRPC is the application API layer, and it fits because the site's clients are its own React components rather than third parties. tRPC gives end-to-end type safety between the database queries and the components that render them, so a change to the schema surfaces as a type error in the component rather than as a runtime error on a listing page. The package scripts show a matching concern with the API, with a `docs:generate` script that runs `tsx src/scripts/api/generate-api-docs.ts` and a `docs:watch` variant that watches the server API routers, the features and the schemas directories.

Clerk handles authentication, which is the reason the project does not have to build user accounts, sessions and password resets. The `.env.example` shows the shape of that, with a publishable key, a secret key and a webhook secret, and the site also runs a mobile app, given by the beta URL pointing at a Google Play listing under the package id `com.producdevity.emureadyapp`.

The testing story is split in the usual way for a Next.js application. Vitest for unit tests and Playwright for browser tests, with a dedicated `playwright.config.ts` and a `tests/` directory at the root, and a `vitest.config.mts`. The `check` script is `pnpm lint:fix && pnpm types`, so linting and type checking are one command, and the tooling around it is ESLint with a flat config in `eslint.config.mjs`, Prettier with a `.prettierrc` and a `.prettierignore`, husky with `lint-staged.config.mjs` for commit hooks, and `components.json` for the shadcn-style component setup.

There is also Sentry, with `sentry.edge.config.ts` and `sentry.server.config.ts` in the root, and an `instrumentation.ts` file, which is the standard Next.js hook for it.

Running it locally, and the commands that matter

The prerequisites are short and the setup sequence is six commands.

You need Node.js matching the `.nvmrc`, pnpm enabled through Corepack, PostgreSQL, and Clerk credentials for the authenticated flows.

bash
corepack enable pnpm
pnpm install
cp .env.example .env.local
pnpm db:generate
pnpm db:migrate:dev
pnpm db:seed
pnpm dev

The order encodes the data flow. `db:generate` produces the Prisma client, `db:migrate:dev` applies the migrations to your local database, `db:seed` loads the initial data, and only then does `dev` start the app. The seed step matters more in this project than in a typical application, because the reference data is the catalogue: games, hardware and emulator entries that reports attach to. Without a seeded database there is nothing to report against.

The shorter command list covers the daily loop.

bash
pnpm dev          # Start the development server
pnpm check        # Run lint fixes and type checks
pnpm test         # Run unit tests
pnpm test:e2e     # Open the Playwright test runner
pnpm build        # Build the application

The `test:e2e` script opens the Playwright runner rather than running headless in CI, which is a deliberate choice for a project whose end-to-end tests exercise a database-backed application.

The package scripts are also where the migration workflow is visible, and it is more elaborate than a single Prisma command. There are `db:migrate:create` for a migration that is created but not applied, `db:migrate:deploy` for applying existing migrations, `db:migrate:status`, `db:push` for pushing the schema without a migration, `db:seed` and a separate `db:seed:permissions`, and `db:backup` which runs a shell script at `./scripts/db-backup.sh`. Almost all of them go through `./scripts/db-cmd.sh`, which is a wrapper rather than a direct Prisma call, and the permissions seed being separate from the general seed says that roles are distinct from catalogue data.

The `build` script is `pnpm db:generate && next build`, so the client generation is part of the build, and there are parallel Turbopack variants for build, dev and analysis, plus a `dev:tracing` variant that sets `NEXT_TURBOPACK_TRACING=1` and a `dev:profile` variant that adds `NEXT_CPU_PROF=1`. Those are the flags you reach for when a Next.js development server is misbehaving, and having them as scripts means the flags are already written down.

The version in the manifest is 0.15.0 with the package manager pinned to [email protected], and the recorded releases are v0.14.0 from 2026-06-04, v0.13.0 from 2026-05-13 and v0.9.0 from 2025-08-08.

Self-hosting, and the Docker setup

The repository is built to be run by someone else, not just by the maintainer. There is a `docs/DOCKER.md`, a `docs/SELF_HOSTING.md` for the production container contract, and a compose file plus a multi-stage Dockerfile.

The compose file defines a PostgreSQL service and the application service. The database is `postgres:15-alpine` named `emuready-postgres`, with a development database called `emuready_dev`, a user and a password, a named volume for the data directory, and a healthcheck.

yaml
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U emuready -d emuready_dev"]
      interval: 5s
      timeout: 5s
      retries: 5

The application service depends on that healthcheck with `condition: service_healthy`, which means the container waits for the database rather than starting and failing. The compose file also sets `DATABASE_URL` and `DATABASE_DIRECT_URL` to a connection string pointing at the `postgres` service by name, which is the standard compose service discovery, and it passes `DOCKER_CONTAINER: "true"` so the application knows it is running in a container.

The seeding logic in the compose file is the part that shows the intended first-run experience. The container command waits for the database, runs `prisma migrate deploy`, and then checks for a flag file, `if [ ! -f .setup/seeded ]`, before running the initial database seed. The `setup_data` volume is mounted at `/app/.setup` specifically to hold that flag, so the seed runs once and not on every container start.

The Dockerfile explains a decision that would otherwise be baffling. The base image is `node:22-bookworm-slim`, and the comment says the reason: the repository pins linux-x64-gnu native binaries for sharp, rollup, tailwindcss/oxide and lightningcss that do not resolve on Alpine or musl. So the glibc base is not a default, it is a consequence of native dependencies that need a glibc host.

The Dockerfile has a `dev` stage, retained for the compose file and a `scripts/docker-dev.sh`, which installs with `--frozen-lockfile --ignore-scripts` and generates the Prisma client at build time. And there is a `builder` stage with a comment that says the build database must be migrated and disposable because Prisma TypedSQL inspects it, with separate `BUILD_DATABASE_URL` and `BUILD_DATABASE_DIRECT_URL` build arguments. That is a real constraint on the build: generating the typed SQL client requires a live database, so the image build needs a database to talk to.

The `dev` stage also shows the source-mounting approach, with the repository copied to `/app` and a separate node_modules volume to keep the host's modules out of the container.

The configuration surface and the external services

The `.env.example` file is the clearest statement of what this project depends on, and it is a long one.

The database is PostgreSQL, and the example carries a useful operational note: persistent servers should use the Supabase session pooler on port 5432. The sample URL points at a Supabase pooler with `sslmode=require` and `uselibpqcompat=true`, and a `connection_limit=5` on the pooled URL that the direct URL does not have. A SQLite file is present but commented out, which tells you SQLite is a fallback rather than a supported production path.

The game metadata comes from several external APIs, each with its own key. There is a Steam Web API key, required for Steam App ID search, and the comment links to where to get one. There is a RAWG API key, a The Games DB API key, and an IGDB client id and secret. Four different catalogues, which tells you the games side of the schema is normalised around titles and external identifiers rather than owning the data.

Authentication is Clerk, with a publishable key, a secret key and a webhook secret. Cloudflare Turnstile is configured with a site key and a secret key, which is the anti-bot check on the public submission form, and the keys in the example are Turnstile's own test values.

Then there is a long tail of `NEXT_PUBLIC_` flags that are effectively feature switches, and the Dockerfile build arguments list the same set, which tells you they are baked into the build rather than read at runtime. There is `NEXT_PUBLIC_ENABLE_ANDROID_DOWNLOADS`, `NEXT_PUBLIC_ENABLE_ANALYTICS`, `NEXT_PUBLIC_ENABLE_KOFI_WIDGET`, `NEXT_PUBLIC_ENABLE_PATREON_VERIFICATION`, `NEXT_PUBLIC_ENABLE_SENTRY`, `NEXT_PUBLIC_ENABLE_SW` for a service worker, `NEXT_PUBLIC_DISABLE_COOKIE_BANNER`, and `NEXT_PUBLIC_APP_ENV` which the example sets to local and documents as one of local, test, preview, staging, production.

The pattern of public flags for optional integrations is worth noting for a self-hoster, because it means the surface you can turn on or off at build time is fairly large and the defaults are conservative, with analytics and the funding widgets off.

There is also an email section that is currently aspirational, with `EMAIL_ENABLED=false` and a commented-out block for two different providers, which is a reasonable way to carry a planned feature without committing to a vendor.

What the project is good for, and where its data stops

The limits of EmuReady are the limits of its data, and they are worth stating before anyone builds on it.

The central one is that every fact in the catalogue is a user's account of their own setup. A report of a game running at a frame rate on a device with a given emulator version is a claim, not a measurement, and it is filtered through what the user chose to record. The site's answer to that is the settings and notes fields that make a report reproducible, and the voting and moderation tools that let the community correct each other. Those are the right mechanisms, and they are also the ones that only work if enough people use them carefully.

The performance categories are the second limit. A report that says a game runs well is only meaningful relative to a defined scale, and a handheld with a power-limited SoC and a desktop with a discrete GPU are not comparable on the same scale. The filter set of CPU, GPU, device, system, emulator and performance is what lets a user make the comparison themselves, and that is the honest way to present the data: as a set of observations, from which the reader draws the conclusion.

The third limit is coverage. A database is only as useful as its least covered corner, and the practical effect is that for a specific handheld and a specific emulator version, the number of reports may be zero or one. The browsing filters make that visible, which is the right behaviour, but it does mean the site is strongest for popular combinations and thin everywhere else.

The alternatives are three, and they differ in where the data comes from. The first is the community wiki or forum for a particular emulator or handheld, which has the same coverage problem but none of the tooling. The second is the compatibility list published by the emulator project itself, which has more authority on a small number of titles and none of the community breadth. The third is testing it yourself, which gives you the answer for your exact configuration and none of the catalogue.

What EmuReady adds over all three is the comparison. The reason to use a database rather than a wiki page is that you can filter it, so you can find reports for your device specifically rather than for the device in general, and you can see how many people reported the same thing, which is a crude but real confidence signal.

The project is also worth noting for its own sake. It is a self-hostable Next.js and PostgreSQL application with a documented Docker path, a production container contract and a permissive copyleft licence, which means a community, a university or a hardware vendor can run their own instance with their own moderation rules and their own catalogue. That is a more useful property than the hosted site alone.

Editorial conclusion

EmuReady suits someone who wants a filtered answer to whether a game runs on a device they own, and a project team that wants to host its own compatibility database with its own moderation rules. It does not suit a project that needs authoritative compatibility data, because every report is a user's account of their own setup. Verify first that the device and emulator combinations you care about have enough reports to be useful, that the performance categories in the reports mean the same thing on your hardware as on theirs, and that the deployment you pick handles the Clerk keys and the external game database API credentials in a way you can control.

Frequently asked questions

What is EmuReady?

It is a community-driven platform for tracking how games run across emulators, handhelds, mobile hardware and PC configurations. Users share compatibility reports, compare setups, discuss results and keep the catalogue useful through voting and moderation, and the production site is at emuready.com.

How do I set up EmuReady locally?

You need Node.js matching the .nvmrc, pnpm through Corepack, PostgreSQL and Clerk credentials. Then run corepack enable pnpm, pnpm install, copy .env.example to .env.local, and run pnpm db:generate, pnpm db:migrate:dev, pnpm db:seed and pnpm dev, and open http://localhost:3000.

What does EmuReady use for its database and API?

PostgreSQL with Prisma for the data layer and typed access, and tRPC for the application APIs, with Clerk handling authentication, Tailwind CSS for styling, Vitest for unit tests and Playwright for browser tests.

Can I self-host EmuReady?

Yes. The repository ships a multi-stage Dockerfile and a compose file with a PostgreSQL service and the application, and the documentation covers Docker-specific setup and the production container contract in docs/DOCKER.md and docs/SELF_HOSTING.md. The build needs a disposable migrated database because Prisma TypedSQL inspects it.

What data does EmuReady pull from other services?

Game metadata from several catalogues, with a Steam Web API key for App ID search plus RAWG, The Games DB and IGDB credentials, alongside Clerk keys for authentication and Cloudflare Turnstile keys for the bot check on submissions.

Official sources

  1. License: GPL-3.0
  2. Producdevity/EmuReady on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/producdevity-emuready.svg)](https://hysenlabs.com/projects/producdevity-emuready)

Was this useful?