Test your Fire TV game on every Fire TV, even if you don't own one.
FireLab makes your game see any Fire TV model's GPU (its WebGL version, texture limits and extensions) on a stick you already have, in an emulator, or in desktop Chrome with no Fire TV at all. Then a game-testing engine plays the game with the remote, finds crashes, black and frozen screens and D-pad focus traps, and hands you each bug as the shortest key sequence that reproduces it, with a measured reproduction rate. Free, local and MIT-licensed.
Website and videos · Getting started · Benchmark · GPU emulation · MCP tools
You need Node 22+ and Chrome (Edge, Brave or Chromium work too). Start your game the way you always do, then:
npx -y firelab explore --driver browser --url http://localhost:8080/ --minutes 10FireLab plays it with the remote's keys at the Fire TV's 960×540 viewport and writes an HTML report: crashes, uncaught errors, black and frozen screens and D-pad focus traps, each with the shortest key sequence that reproduces it.
| Your app | How FireLab tests it |
|---|---|
| Web game or web app (three.js, Phaser, React, Vite, plain HTML…) | --driver browser --url <page>: headless Chrome, no device |
| Fire OS or Android TV app (APK, incl. WebView wrappers) | firelab connect <tv-ip>, then --package <app.id> or --apk app.apk: a real stick or an emulator |
Vega OS app (.vpkg) |
--driver vda through the Vega SDK: experimental, not yet verified on a real Vega device (docs/VEGA.md). If the app has a web build, test that in browser mode too. |
Not sure what's set up? npx -y firelab doctor lists what's ready and the
exact command for each. With an AI assistant: claude mcp add firelab -- npx -y firelab mcp,
then ask "Use FireLab's browser driver to explore http://localhost:8080/ for 10 minutes and fix what it finds."
There are 66 Fire TV models in the wild, from the 1 GB Fire TV Stick with a Mali-450 GPU and no WebGL2 to the 4K Max. A developer owns one of them, or none. So the bugs that hurt TV games ship: a shader that compiles on your stick but not on a Mali-450, a texture atlas bigger than the GPU allows, a focus trap three menus deep, a black screen after a fast double press on OK, a crash on the 1 GB stick. Testing it by hand means pressing the same remote buttons for hours, on hardware you don't have.
- Emulate any Fire TV's GPU, without that Fire TV. Pick a model (the
Fire TV Stick 2nd gen, the 4K Max, a Toshiba Fire TV, 60+ in all) and
your game's WebGL reports only what that GPU supports: no WebGL2 on a
Mali-450, its real
MAX_TEXTURE_SIZE, its extension list, its shader precision. Calls the real GPU would refuse fail the same way, and FireLab lists them. It works on a real stick, in the emulator lab, or in desktop Chrome as a userscript, so a developer with no Fire TV can still find the "breaks on that stick" bugs. (docs/GPU.md) - A game-testing engine built on the research, measured like the research.
firelab exploreis Go-Explore adapted to a TV remote: an archive of screens, return-to-promising-states, fast double presses and long holds, a D-pad focus oracle that walks every screen's focus graph, and a minimiser (loop cutting, delta debugging) that turns a 37-key path into 8 keys. It is benchmarked against uniform random input and Android's Monkey with the standard method for randomised testers (repeated runs, Mann-Whitney U, Vargha-Delaney Â12). We found no published exploration benchmark for TV or HTML5 games, so we published one. - A go / no-go verdict for every Fire TV model from one stick.
firelab fitmeasures your game once and judges 66 models on memory, GPU features, video decoders and Android version, marking which verdicts are measured and which are estimates. - Fire-TV-shaped emulators and CI.
firelab labbuilds Android emulators with a model's RAM, heap, screen and Android version, and the GitHub Action can run smoke tests and the engine on every pull request. - An AI agent can test the TV. The MCP server gives Claude Code, Kiro, Cursor or VS Code 25 tools to see the screen, press the remote, read the WebView console, measure, and run the engine.
All recordings are real: a Fire TV Stick (3rd gen, 1 GB, Fire OS 7) on our desk, recorded on 19 Oct 2026. They play on the website; click a picture to open the video file.
| What | Result | Evidence |
|---|---|---|
| Bugs reported, Firefly Lake (5 seeded) | FireLab 4, Monkey 2, Random 1 (median of 5 × 10 min; Â12 = 1.00, p ≈ 0.01) | benchmark |
| Game code covered, Firefly Lake | FireLab 120 JS blocks, Monkey 79, Random 74 | benchmark |
| Screens reached, Deep Quest (30-floor RPG) | FireLab 246, Random 82, Monkey 70; deepest 36 keys vs 13.5 | benchmark |
| A real bug on a real Fire TV | Back quit the app on busy pages: 37 keys shrunk to 8, reproduced 5/5 on a 4K Plus; after the fix 0/5 on a 3rd gen and the emulator | friction log, entry 3 |
| No Fire TV at all: explore in desktop Chrome as a Fire TV Stick 2nd gen | Found that Firefly Lake breaks on that stick: getContext("webgl2") refused on the Mali-450, Error: WebGL2 not available, render loop stopped |
report |
| GPU check on a real stick as a 2nd gen | WebGL2 refused on the Mali-450 | result |
| Explore on a 1 GB Fire TV Stick (3rd gen) | 44 steps, 4 game states in 3 min at ~3 s per key | report |
| Test suite | 830 automated tests passing (594 + 236 in @firelab/perf) |
npm test |
The benchmark games and their bugs were written by FireLab's authors and five runs is a laptop budget, so treat the p-values as indicative. The limits are in docs/BENCHMARK.md.
What got in our way building and testing Fire TV games on real hardware, in the hackathon's format (task, steps, expected vs actual, severity, workaround, suggestion). Every number was measured on the device named, and re-checked against our run data and logs before publishing. Full log: docs/FRICTION-LOG.md (18 entries and a test-session log) and docs/FRICTION-LOG-FIRELAB.md (24 rough edges in FireLab itself, found by dogfooding it, with what we fixed).
| # | Friction | Measured | Severity |
|---|---|---|---|
| 1 | adb shell input keyevent is slow, so fast double presses can't be tested |
1.10–1.36 s per key on a 4K Plus, 1.03–1.86 s on a 3rd gen; one batched call of 3 keys: 1.29 s | High |
| 3 | No documented way for a WebView TV app to say "I handled Back"; the obvious approach races | Back quit the app 5/5 on a 4K Plus; FireLab's fix: 0/5 on a 3rd gen and the emulator | High |
| 6 | No Fire OS emulator; the Android emulator can't match a 1 GB stick | asked for 1024 MB, got 2473 MB; no arm64 TV image below API 31 | High |
| 12 | "Play on your phone" party games pick the same ports and crash each other | BindException: Address already in use, reproduced 5/5 by firelab explore |
High |
| 14 | The emulator renders with a software GPU, nothing like a Fire TV's | WebView 133 on SwiftShader vs PowerVR GE8300 and WebView 138 on the stick | High |
| 15 | No per-model GPU / WebGL capability table | spec pages name the GPU, not WebGL2, texture limits or formats | High |
| 4 | Screenshots are slow | full PNG 2.1–2.5 s; FireLab's compressed path 1.2–1.4 s | Medium |
| 5 | Release builds can't be inspected, so most installed games can't be tested deeply | 5 of 14 games on a 3rd gen were debuggable; 3 of those still had WebView inspection off | Medium |
| 9 | Per-model memory budgets aren't published | a "1 GB" stick reports 901 MB | Medium |
| 13 | Games that draw their own highlight are invisible to the accessibility tree | uiautomator kept one focused node while the highlight moved |
Medium |
| 17 | The obvious Monkey setting for D-pad apps never presses OK | --pct-nav 100 sends only arrows; OK, Back and Menu are majornav |
Medium |
FireLab was built during Build, Ship, Shape: Amazon Developer Hackathon 2026 and is entered in the Open Source mini challenge, alongside our Fire TV track game, Sunset Villa, which FireLab was first built to test.
- What we built in the hackathon window: the engine, GPU emulation,
fit, the emulator lab and GitHub Action, the dashboard, the MCP server,wrapand@firelab/perf. - Friction logs in the rules' format (task, steps, expected vs actual, severity, workaround, suggestion): FRICTION-LOG-FIRELAB.md and FRICTION-LOG.md.
- Research behind the engine: docs/RESEARCH.md and the design in docs/ENGINE.md.
npx -y firelab demo # the engine plays a simulated TV game with seeded bugs, then opens the reportOr from source: git clone https://github.com/AkilSaji/firelab.git && cd firelab && npm install && npm run demo.
It takes about 5 seconds and needs only Node 22+: no Fire TV, no adb, no Android
SDK. Or skip installing:
and run
npm run demo there. npm run demo:dashboard shows the live dashboard
with a simulated TV.
See a real report first: the report from a real Fire TV Stick 4K Plus (10 minutes on Firefly Lake with seeded bugs; also in docs/sample-run/).
With an AI assistant, connect FireLab (step 3 below) and ask: "Run FireLab's explore on the demo driver and explain what it found."
New here? Start with Getting started: test your app on a Fire TV. It takes you from a new TV to a bug report, in order: connect, install, watch,
explore, performance, other models (fit), emulators and CI, and AI agents.
Test and tune apps and games on real Fire TVs, with AI agents and with a live dashboard.
FireLab drives Amazon Fire TV (Fire OS) and other Android TV devices over adb. You can use it three ways:
- An MCP server, so Claude Code, Kiro, Cursor or VS Code can see the TV, press the remote, read the WebView's console and measure frame rate.
- A CLI, for scripts and CI.
- A dashboard, for people.
It also finds the best quality tier for your web game on each device, and it can record a person's play session.
- Find and keep devices. It scans the Wi-Fi for TVs with adb, connects to them, and reconnects by itself when a Stick drops off. It knows Fire TV models by name ("AFTMA08C15" is the Fire TV Stick 4K Plus), their Fire OS version, CPU ABI, RAM and GPU, and the quality tier that suits them.
- Remote control. D-pad, OK, back, home, media keys and text, sent in
one batched call. Real holds (
right:1500) go through a virtual HID remote that FireLab builds with Android's ownhidtool, with no root and nothing to install. - Screenshots that just work. FireLab strips the warning text that some Fire OS builds print before the PNG. Its fast path (raw framebuffer through the device's gzip) takes 1.1 s instead of 2.5 s per 1080p frame on a Fire TV Stick 4K Plus.
- See inside WebViews. It evaluates JS, captures console errors and
exceptions, reloads or navigates the page, and counts frames. All of this
goes through Chrome DevTools over
adb forward, with no Chrome needed. - Frame rate you can trust. FireLab reads
window.firelabwhen the page publishes it. Without it, an injected probe counts frames that actually drew WebGL, so a game capped at 30 fps reads 30, not 60. On a real three.js game the probe was within about 1 fps of the game's own counter. For native and Unity apps, FireLab reads SurfaceFlinger frame timing. It also reports memory (app plus WebView renderer) and CPU. - The sweep. It loads your game at every quality tier, measures it, and recommends the richest tier that holds 30 fps on this device, with a plain-English reason.
- Watch a person play. It records every remote press with its hold time, plus fps, memory, errors and a thumbnail every 10 s, and gives you a summary at the end.
- Apps. List, install (it checks the APK's ABI and minSdk before adb fails cryptically), launch with Android's own launch time, force-stop, reset (only with explicit confirmation), and logcat filtered by app.
MIT licensed. No telemetry. Tested on macOS; Linux and Windows are supported (adb discovery and paths are handled) but not yet tested on real hardware.
1. On the Fire TV: go to Settings > My Fire TV > About and select the device name 7 times. Then go to Developer options and turn ADB debugging on. Note the IP address under About > Network.
2. On your computer: you need Node 22+ and adb (brew install --cask android-platform-tools, sudo apt install adb, or
winget install Google.PlatformTools).
Install FireLab:
npm install -g firelab # or prefix every command with `npx -y`
firelab connect 192.168.1.50 # then press "Allow" on the TV, tick "Always allow"
firelab doctor # checks everything, explains any fixFrom source instead: git clone https://github.com/AkilSaji/firelab.git && cd firelab && npm install && npm run build && npm link.
3. Connect your AI assistant (MCP). FireLab runs as an MCP server on your own computer: the assistant starts it, and it talks to the TV over adb. Nothing goes to a cloud service. Add it once:
Claude Code
claude mcp add firelab -- npx -y firelab mcp # or `-- firelab mcp` after npm install -gor in .mcp.json in your project:
{ "mcpServers": { "firelab": { "command": "firelab", "args": ["mcp"] } } }Claude Desktop: Settings > Developer > Edit Config (claude_desktop_config.json)
{ "mcpServers": { "firelab": { "command": "firelab", "args": ["mcp"] } } }Claude Desktop may not see your shell's PATH: if it can't start firelab,
use the full paths, e.g. "command": "/usr/local/bin/node", "args": ["/path/to/firelab/dist/cli.js", "mcp"].
Kiro: .kiro/settings/mcp.json
{ "mcpServers": { "firelab": { "command": "firelab", "args": ["mcp"] } } }Cursor: .cursor/mcp.json
{ "mcpServers": { "firelab": { "command": "firelab", "args": ["mcp"] } } }VS Code: .vscode/mcp.json
{ "servers": { "firelab": { "type": "stdio", "command": "firelab", "args": ["mcp"] } } }Without npm link
Point the config at the built CLI directly:
{ "mcpServers": { "firelab": { "command": "node", "args": ["/absolute/path/to/firelab/dist/cli.js", "mcp"] } } }Any client over HTTP (one server for several clients)
firelab mcp --http 7301 # Streamable HTTP at http://127.0.0.1:7301/mcpThen give your client the URL http://127.0.0.1:7301/mcp, e.g.
claude mcp add --transport http firelab http://127.0.0.1:7301/mcp. It only
listens on this computer (127.0.0.1).
If adb isn't on your PATH, add "env": { "ADB": "/path/to/adb" } to the
server config. Restart the assistant after changing its config; then ask it
"list my Fire TV devices" to check the connection.
4. Ask your assistant:
Find bugs in my game dev.example.mygame on the Fire TV: explore it for 15 minutes, then tell me what broke, how to reproduce each problem, and what to fix.
Take a screenshot of the TV, open my game com.example.game, play the first level with the remote, and tell me what looks wrong on a 10-foot screen.
Run a performance sweep of http://192.168.1.20:8080/ and tell me which quality tier to ship for this device.
More copy-paste prompts (no Fire TV / emulator setup, fix-and-verify, web
games, native APKs, performance, every-model fit, CI) are in
docs/PROMPTS.md. The explore_app, playtest and
tune_performance prompts walk an assistant through each. All 25 tools are listed in docs/TOOLS.md.
Without a global install, use "command": "npx", "args": ["-y", "firelab", "mcp"] in any of the configs above.
Add @firelab/perf to your web game:
const perf = createPerf();
perf.attach(renderer);
// … in your frame loop:
perf.frame();It detects the GPU and picks a tier (low / tv / tv-hd / high). It
holds 30 fps by adapting the render resolution, and shows a meter when you
add ?perf=1 to the URL. It also publishes window.firelab, which
firelab sweep reads. See
packages/perf/README.md.
firelab devices --scan --connect # find Fire TVs on the Wi-Fi and connect them
firelab info # model, Fire OS, ABI, RAM, GPU, tier, power, foreground app
firelab screenshot -o tv.png
firelab keys down*3 ok # also: ok:long, right:1500, back, home, playpause ...
firelab launch com.example.game
firelab eval "document.title" --package com.example.game
firelab measure --seconds 10 # fps median/p10, memory, CPU of the foreground app
firelab sweep --url http://192.168.1.20:8080/ --tiers low,tv,tv-hd,high
firelab watch --minutes 10 # record a play session (Ctrl-C to stop)
firelab logcat --package com.example.game --level warn
firelab ui # the dashboard-d <serial|ip> picks a device when more than one is connected, and --json
gives machine-readable output. firelab help lists everything.
firelab ui opens http://localhost:7300. It has:
- a live mirror of the TV screen
- a virtual remote (your keyboard works too, and holding a button holds it)
- live fps, memory and CPU charts
- the WebView console, mixed with the app's logcat errors
- an apps panel (launch, stop, reset, install an APK)
- the sweep runner
- recorded play sessions with their key timeline and thumbnails
Behind it is a small HTTP and SSE API (see docs/CONTRACT.md), so you can script it too. The API listens on localhost only and refuses cross-site requests.
Web games on TV need different settings per chip. A three.js scene that runs
at 60 fps on a laptop can drop to 13 fps on a 1 GB Fire TV Stick with
shadows on. firelab sweep (or the performance_sweep tool, or the
dashboard's Sweep tab) does the following:
- Loads your page with
?q=low,?q=tv,?q=tv-hdand?q=highin turn. The parameter name and the tiers can be changed. - Optionally presses keys or runs JS after each load, to reach a busy scene.
- Warms up, then measures fps every second, plus memory and CPU.
- Recommends the richest tier that holds the target: a median of at least 30 fps (within 5%, because frame-capped games jitter a vsync now and then) and a p10 of at least 24 fps, so it doesn't stutter.
- Restores the page and saves JSON and Markdown to
~/.firelab/sweeps/.
Try it with the example game: run node examples/web-game/serve.mjs,
open the address it prints on the TV, then run:
firelab sweep --package dev.firelab.fireflylake --url "http://<ip>:8080/examples/web-game/?autoplay=1"Result from a real run on a Fire TV Stick 4K Plus (AFTMA08C15, PowerVR GE9215, Fire OS 8.1.8.2) with the three.js game Sunset Villa:
Pending. The run is scheduled on the test device; it will be pasted here from
npm run device:check -- ... --sweep(see Tested on). The table looks like this:
Tier Median fps p10 fps Memory Render scale Holds target tv … … … MB – … tv-hd … … … MB – … high … … … MB – … Recommendation:
<tier>.<tier>is the richest tier that holds 30 fps here (median … / p10 … fps).<next>drops to median … / p10 … fps.
Live numbers that FireLab has already measured on that device and game: 24.6–27.5 fps (counted from frames that drew WebGL), 484 MB for the app plus its WebView renderer, and about 135% of one CPU core while exploring. The probe agreed with the game's own frame counter to within about 1 fps.
@firelab/perf is a tiny, dependency-free browser library
for TV web apps and games. It:
- detects the GPU and picks a tier
- holds your target fps with adaptive resolution
- shows an on-screen meter
- publishes
window.firelab, so the tester reads the game's own numbers
import { createPerf } from '@firelab/perf';
const perf = createPerf(); // detect the GPU, pick a tier ('low' | 'tv' | 'tv-hd' | 'high')
perf.attach(renderer); // three.js: adaptive resolution drives setPixelRatio()
renderer.setAnimationLoop(() => { renderer.render(scene, camera); perf.frame(); });There is a demo game in examples/web-game: Firefly
Lake, a small three.js game that uses @firelab/perf end to end. Run it with
node examples/web-game/serve.mjs.
firelab wrap turns any URL into a Fire TV / Android TV APK, without Gradle:
firelab wrap --url http://192.168.1.20:8080/ --name "My Game" [--install <device>]It needs Android build-tools and a platform
(sdkmanager "build-tools;35.0.0" "platforms;android-35") plus JDK 17. Run
firelab wrap --check to see what's missing (firelab doctor checks it
too). In the APK, remote keys arrive as keyboard events. Back sends Escape,
so call preventDefault() on it to handle it yourself. See
android-template/README.md.
firelab explore plays your TV app with the remote, on its own, and hunts for
crashes and ANRs, JS exceptions, black screens, frozen pictures or a stopped
render loop, D-pad focus traps, controls the D-pad can't reach, and arrow keys
that ignore the layout. Every finding comes back shrunk to the shortest key
sequence that reproduces it, with a measured reproduction rate, in one
self-contained report.html (plus report.md for PRs).
firelab explore --package dev.firelab.fireflylake --minutes 10
firelab explore --apk my-game.apk --minutes 10 --fail-on crash,black,frozen,focus-trap --out out/ --json
firelab replay out/traces/f-2aa559.min.trace.json --times 5 # reproduced k/n from a fresh launch
firelab shrink out:f-2aa559 --budget-min 10 # shrink (again) later
firelab report out --baseline <random-run-dir> # rebuild the report, with a baseline curveNo TV? Any web game runs in headless Chrome at the Fire TV WebView's viewport, with remote keys delivered the way FireLab's TV wrapper delivers them:
firelab explore --driver browser --url https://my-game.example/ --minutes 10 --coverageHow it works (details in docs/ENGINE.md):
- Go-Explore on a TV. It keeps an archive of the screens it has seen, picks
a promising one, gets back to it (over the key graph it has learned, or by
relaunching and replaying the shortest known path: a TV has no snapshots),
then tries every remote key there, plus fast repeats (
ok×3) and long presses. - Deep by design. On every new screen it reads the focusable controls and
presses each one in turn, so a button opens a dialog, the dialog's buttons are
next, and so on 100+ presses down. Cells with untried keys and deep cells are
preferred, productive episodes run longer, keys that do nothing are learned
per screen, and with
--coveragea press that runs new JavaScript counts as progress (V8 block coverage). Each idea comes from published GUI-testing work (DroidBot, APE, Stoat, TimeMachine, Go-Explore, Wuji): see ENGINE.md 6.7. - States come from inside the app: one Chrome DevTools probe per step reads
the URL,
window.firelabmarks, the focused element and an optional--state-expr(e.g.demo.ui). Canvas apps with no probe fall back to a visual hash of the screen. - Oracles: health (logcat crashes/ANRs/renderer death, unexpected exits, JS exceptions), screen (black, frozen, render loop stopped, WebGL context lost), focus (on each new screen it walks the D-pad focus graph and checks it against the layout: traps, unreachable controls, non-spatial moves, lost focus), and stuck (dead ends).
- Shrinking: the explorer's own shortcuts first, then loop cutting, delta debugging and gap shrinking, accepting a candidate when it reproduces in replays; then 5 replays decide confirmed (≥ 60 %), flaky or not reproduced.
- CI:
--fail-ontakescrash,black,frozen,focus-trap,js,unreachable,non-spatial,focus,exit, a severity or a finding kind (defaultcritical,major). Exit code 1 when a matching finding didn't fail to reproduce.--jsonprints a summary;report.mdfits a job summary.--urlis the page a wrapped web app opens (it needs--packageor--apktoo; wrap a bare URL first withfirelab wrap). - MCP:
explore_start,explore_status(with the current screen as an image),explore_stop,explore_findings, and anexplore_appprompt. - Vega OS:
--driver vdaonexplore,replayandshrink(MCP:driver: "vda") drives a Vega Fire TV through the Vega SDK'svegaCLI;--apkthen takes a.vpkg. Vega has no JS eval, so states come from the screen and the focus oracle is off, and FireLab sees the app go only when its process ends; the report notes all of this. The Vega driver has not run on a real Vega device or the Vega Virtual Device yet: see docs/VEGA.md. - It learns from everyone who uses it.
firelab learn exportturns your runs into a small knowledge pack: the last keys of each confirmed bug (e.g.ok*3 -> black-screen) and which keys tend to matter. Contribute it with a pull request to knowledge/ and every FireLab run tries those patterns early on new screens. It works the same from the CLI, an AI assistant (MCPlearn_export) or CI. Opt-in, reviewed, no URLs, names, text or screenshots, and nothing is ever uploaded automatically. - Benchmark:
firelab benchruns FireLab, an ablation of its first version, uniform Random and Android Monkey's D-pad event mix on games with seeded bugs (Firefly Lake, and Deep Quest: a TV RPG with a 30-floor dungeon whose bugs sit from 9 to 100 presses deep), scored on ground truth the games record themselves plus JS coverage, with Mann-Whitney U, Vargha-Delaney Â12 and Fisher's exact test. See docs/BENCHMARK.md. - It refuses to drive
com.sunsetvilla.tv(someone's real progress) unless you pass--allow-package.
Measured on a Fire TV Stick 4K Plus (Fire OS 8), Firefly Lake with
?bugs=all (5 seeded bugs in examples/web-game/bugs.js),
seed 1, 10 minutes each:
| Go-Explore | random baseline | |
|---|---|---|
steps (≈ 2.8 s each: one input keyevent ≈ 1.2 s, one frame grab ≈ 1.8 s) |
178 | 166 |
| distinct states | 15 | 11 |
| seeded bugs found | 2 of 5: freeze (render loop stopped after the 2nd lantern), grid (unreachable "Credits" + list-order arrows) | 1 of 5: freeze |
| other findings | Back exits the app when the page is busy, even though the page handled it (a race in FireLab's own wrapper, now fixed; see below) | the same Back race (4 findings: random presses Back often) |
| shortest repros | menu playpause ok down up (freeze, flaky 2/5: rock spawns are random), menu (Credits, 2/5), menu up up up (non-spatial, 5/5), menu up up up left right up back (Back race, 5/5, from 37 keys) |
(not shrunk) |
One run per strategy, so treat the comparison as preliminary (ENGINE.md 12.3).
Missed in the Go-Explore run: black (needs ok×3 on Resume; the run sent one macro
in 178 steps, and firelab replay of menu ok×3 does blank the screen 2/2),
throw (needs ▶ then ◀ on Quality) and trap (the focus walk saw the
trapped group, but Back left the app instead of doing nothing, because of the
wrapper race, since fixed). On a simulated version of the same game (test/engine/), with
10 minutes of simulated device time, the explorer finds all 6 seeded bugs and
shrinks five to at most 6 keys; the random baseline finds 3 and never the
focus trap, the unreachable control or the black screen. Real TVs are slow:
expect a few hundred steps per 10 minutes, and give exploration 30+ minutes
for deep bugs.
The engine found a real bug in FireLab itself. In wrapped web apps
(firelab wrap), Back sometimes quit the app even when the game had handled it:
the wrapper recorded "the page used Back" in an async callback that could run
after the key-up check when the page was busy. The engine shrank a 37-key random
path to 8 keys (menu up up up left right up back) that reproduced it 5/5 on
the Fire TV Stick 4K Plus, and 2/3 with the seeded bugs off. Fixed in
android-template (the flag is now set inside the key-down script). Verified
with firelab replay on a real Fire TV Stick 3rd gen: the old wrapper quits
2/5 (seeded bugs on) and 2/3 (off), the fixed one 0/5 and 0/5. On an
AFTSSS-shaped emulator: old 4/5, fixed 0/5 and 0/5; Back on the title screen
still exits. Rebuild wrapped apps with firelab wrap to get the fix.
On 2026-10-18 FireLab explored 11 shipped party games (a games hub and ten games from one studio) on a Fire TV Stick 3rd gen (AFTSSS, 1 GB, Fire OS 7.7.1.7): 8–10 minutes each, about 1,000 key presses in total. None of these builds allow WebView inspection, so FireLab tested them from the outside: crashes, ANRs, black and frozen screens, keys that quit the app, and D-pad focus where Android exposes it.
| Game | What FireLab found | Repro |
|---|---|---|
| Game A | Crash on Back: NullPointerException while the activity is destroyed |
1 key (back), 5/5 |
| Game B | Crash on launch: java.net.BindException: Address already in use |
5/5 |
| Game C | Same crash on launch | 4/5 |
| Games D–J and the games hub | No crashes, ANRs, black or frozen screens |
The two launch crashes have one cause: each party game opens its own "play on your phone" web server on a fixed port (8765). With another party game still running in the background, as Fire OS leaves it after Home, the next one can't bind the port and crashes. FireLab confirmed it by launching each game alone (works) and after another game (crashes).
These runs also made FireLab better. Exploring games that can't be inspected showed four sources of false findings: frozen-screen alarms when a dialog pauses the background, "Back left the app" on a game's first screen, a games hub opening a game, and "unreachable" buttons in a game that draws its own highlight. All four were fixed the same day, with tests, and the re-runs came back clean. Details are in docs/FRICTION-LOG.md, entries 12 and 13.
firelab lab makes an Android emulator shaped like a real Fire TV model: the
same Android API level as its Fire OS, the same RAM and heap, and the same
screen and density. Every FireLab command then works on it as if it were a TV.
firelab lab doctor # SDK, emulator, KVM/HVF, system images (prints install commands)
firelab lab create --profile AFTSSS --install # Fire TV Stick (3rd gen): Android 9, 1 GB, 1080p
firelab lab start --headless # boots it and connects it like a device
firelab lab smoke --apk my-app.apk # install, launch, screenshot, no crash/ANR, memory vs the stick's budget
firelab explore --apk my-app.apk # the game-testing engine works here too
firelab lab stopIn CI, the FireLab lab action does all of this on
GitHub's ubuntu-latest (it has KVM), uploads the HTML report and writes a
job summary:
- uses: AkilSaji/firelab/action@v0
with:
apk: app/build/outputs/apk/debug/app-debug.apk # or url: https://my-game.example/
profile: AFTSSSThe emulator matches a Fire TV's memory, screen and API level, not its
Mali/PowerVR GPU, ARM CPU or Amazon WebView, so its frame rates mean nothing
for a real stick: use firelab fit with the real profiles for performance.
See docs/LAB.md.
What the lab can copy of the GPU is its features and limits, which are
exact per chip: --emulate-gpu <model> makes WebGL report only what that
model's GPU has (WebGL2 or not, MAX_TEXTURE_SIZE, extensions, ASTC/ETC/S3TC,
shader precision, the renderer string) and lists the WebGL calls that fail, so
you see where a web game breaks on a stick without the stick. It copies
features, not speed. It works on the lab, a real device or desktop Chrome:
firelab fit --emulate-gpu AFTT --url http://10.0.2.2:8080/ -p dev.firelab.mygame # failed WebGL calls on a Mali-450 stick
firelab explore -p dev.firelab.mygame --emulate-gpu AFTT # the engine plays it; each failure is a finding
firelab fit --emulate-gpu AFTSSS --script gpu-AFTSSS.js # the shim for desktop ChromeSee docs/GPU.md.
Claude Code / Kiro / Cursor / VS Code you, in a browser scripts / CI
│ MCP (stdio or HTTP) │ HTTP + SSE │
▼ ▼ ▼
src/mcp (25 tools, 3 prompts) src/http (dashboard API) src/cli.ts
└────────────────────┬─────────────────┴───────────────────────────┘
▼
src/core FireLab (devices, sessions, jobs)
┌──────────────────────┼─────────────────────────────┐
▼ ▼ ▼
src/adb: adb client src/cdp: DevTools over src/perf: meminfo, /proc,
discovery, reconnect, adb forward: eval, console, SurfaceFlinger, measure,
keys + HID remote, navigate, fps probe sweep, watch sessions
screenshots, apps, logs (tiers: @firelab/perf/chips)
└──────────────────────┴─────────────────────────────┘
▼
adb ──Wi-Fi/USB──▶ Fire TV / Android TV
Everything that parses device output is tested against output recorded from
real devices (test/fixtures/). The MCP tools (over stdio-style in-memory and
Streamable HTTP transports), the HTTP API, the DevTools client (against a fake
DevTools server), the virtual remote and the sweep logic all have tests that
need no device; the game-testing engine runs against a simulated TV game.
npm test runs 553 Vitest tests plus 236 node:test tests for @firelab/perf.
| Device | Model | OS | GPU | Status |
|---|---|---|---|---|
| Fire TV Stick 4K Plus (2025) | AFTMA08C15 | Fire OS 8.1.8.2 (Android 11) | PowerVR GE9215 | Verified 2026-10-15/16. Device detection and info, doctor, apps, screenshots (raw+gzip 1.1–2 s, PNG 2.5 s), WebView eval, the fps probe against the game's own counter, logcat, keys (input keyevent; no HID holds on Fire OS 8), launch/stop, wrap --install, the stdio MCP server, the dashboard with its live stream, and the game-testing engine (explore, replay, shrink: about 2.8 s per step). Pending on a real TV: sweep and watch (both verified end to end on an AFTSSS-shaped emulator, 2026-10-18). |
| Fire TV Stick (3rd gen) | AFTSSS | Fire OS 7.7.1.7 (Android 9) | PowerVR GE8300 | Verified 2026-10-18. Connect, info, screenshots, keys, launch/stop, wrap --install, sweep (tv holds 30 fps), replay (Back fix: old wrapper 2/5 and 2/3, fixed 0/5 and 0/5), the dashboard, and explore on 11 real games (above). Pending on a real TV: watch. |
The computer was macOS 26 (Apple silicon), Node 24.20 and adb 1.0.41
(platform-tools 37.0.1). To add your device, run
npm run device:check -- --device <ip> [--package <webview app> --url <page> --sweep --keys]
and send its "tested on" row in a PR. Even more useful: record your model's
profile with firelab profile -d <ip> and send profiles/<MODEL>.json
(how). Only 2 of the 66 Fire TV models have one so far.
- FireLab never connects to a device on its own: scans only report what they find. Its adb probe never triggers a prompt on someone else's TV.
- Tools that change things say so.
reset_app(clear data) requiresconfirm: true, andinstall_appandstop_appare marked destructive for agents. - The dashboard and MCP HTTP servers bind to 127.0.0.1 and refuse foreign Host headers and cross-origin writes.
- Nothing is sent anywhere: there is no telemetry and no cloud.
Screenshots, sweeps and sessions stay in
~/.firelab.
- docs/GETTING-STARTED.md: the step-by-step journey, and what each check finds for native and web apps.
- docs/TOOLS.md: every MCP tool and prompt.
- docs/PROMPTS.md: copy-paste prompts for AI assistants.
- docs/TROUBLESHOOTING.md: unauthorized devices, Wi-Fi drops, WebView debugging, the screensaver, frame-rate questions.
- docs/CONTRACT.md: the HTTP API, SSE events, the
window.firelabhook and the tier vocabulary. - docs/FRICTION-LOG.md and docs/FRICTION-LOG-FIRELAB.md: what got in the way building on Fire TV, and using FireLab itself, with what we fixed.
- docs/BENCHMARK.md:
firelab bench, the engine against Random and Monkey on games with seeded bugs, with the latest results. - docs/GPU.md: GPU capability emulation for web games
(
--emulate-gpu): WebGL features and limits per model, not speed. - docs/VEGA.md: the Vega OS driver (Fire TV Stick 4K Select and newer). Built from Amazon's docs, not yet tested on a real Vega device.
- CONTRIBUTING.md: how to add fixtures and devices.
MIT © the FireLab authors







