effe-exe/Wardeye

Place the ward. See the table.

TypeScript

3

120 commits

updated Oct 3, 2026

See the code

See what people are saying

SourceMessageScoreDate

Naming every face-up card on a TCG tournament stream, in the browser: a corner detector trained only on synthetic boards, a DINOv2 embedder, game rules as priors (r/computervision)

[Walktrough](https://reddit.com/link/1wwp43t/video/tufney77m9th1/player) Riftbound is Riot's trading card game, and its tournaments stream on Twitch from an overhead table camera. At 1080p a card is about 140 px tall, under dice, sleeves, hands and H.264, so viewers can rarely read the table.…

0

Oct 3, 2026

README

Wardeye

Place the ward. See the table.

A free browser extension for Riftbound streams on Twitch.
Alpha · AGPL-3.0 · a community project by Federico Vietti · effe-exe.github.io

Wardeye is the ward you place on a Riftbound stream. Point at any card on the table to see its name and official image, as the video plays, live or on replay. It works from the video alone, in your browser: no video leaves your computer.

It is made by Federico Vietti, who also makes Gradeon, the AI card pre-grading app. Wardeye shares Gradeon's dark look; it is not a Gradeon product.

How Wardeye works: a walkthrough in 1:21

The walkthrough (1:21, no sound) downloads from the 0.2.2 release. Footage: Riftbound Regional Qualifier Barcelona, grand final, official broadcast.

Riftbound Zone, the Italian Riftbound community, supports Wardeye. Read its article: Wardeye: the free extension that tells you which cards are on the table while you watch Riftbound on Twitch (also in Italian).

Install

Wardeye comes from the Chrome Web Store: Add Wardeye to Chrome. The store has version 0.1.2; 0.2.2, with the plays panel and the settings below, is next.

  • Chrome: open Wardeye's page in the Chrome Web Store and click Add to Chrome.
  • Edge: open the same page in Edge. Edge asks once to Allow extensions from other stores; allow it, then click Get.
  • Pin it (optional): click the puzzle-piece icon in the toolbar, then the pin next to Wardeye. Its toolbar button turns it off and on.
  • From GitHub, in Developer mode: each release has the extension's zip, the same package the store gets, often days before the store has it. Unzip it, open chrome://extensions (or edge://extensions), turn on Developer mode, click Load unpacked and choose the folder. It does not update itself: install the next release when it comes. The models inside are not open source (D-035).

You need desktop Chrome or Edge, version 137 or newer. A recent graphics chip (WebGPU) makes it fast; without one it runs on the processor, much more slowly. The second line of its badge says which.

From source? A build from this repository has the whole extension but not the trained models, which ship only inside the extension's package: the store version and the zip of each release (D-022). On its own it cannot read a table; see For developers.

Use it

  1. Open a Riftbound stream or replay on twitch.tv. Theatre mode and fullscreen work too.
  2. The Wardeye badge on the player says what it is doing: finding the table, then how many cards it has named.
  3. Point at a card on the table. The hover card shows its name, its official image, how sure Wardeye is, and what lies under it, such as gear on a unit.

On the video, each card Wardeye has named is marked at its corners, with its name above it; the card you point at is outlined. The badge shows the engine's timings when you point at it.

Turn it off in a tab with any of these; the toolbar button or Alt+R turns it back on:

  • the power button on its badge;
  • the Wardeye button in the toolbar (it says OFF while it is off);
  • Alt+R (Option+R on a Mac).

Off, the overlay is hidden and nothing is read.

The plays panel (from version 0.2.2): click the list button on the badge. A panel opens beside the page, with three tabs:

  • Match: each player's legend, their runes on the table (and how many of them are exhausted), the cards face up on their side, and the plays, newest first. Click a play and the replay goes back to just before it.
  • Decklists: a box for each player. Paste the player's published deck code or a deckbuilder's export (text, tourney sheet or JSON) and click Use this list. The box says which legend the list names and any line it could not read. A list counts only for the player whose legend it names, whichever box it is in, and it helps name only the cards face up on the table.
  • Settings:
    • On the video: Outlines and names (the default), Outlines only, or Clean, where nothing shows on the stream until you point at a card. Pointing at a card shows it in every view.
    • Performance: how often Wardeye reads the video. Full reads up to 5 frames a second, Balanced 2 and Light 1, for a computer busy with other apps; the lighter levels name cards more slowly.

The plays and the lists stay in the tab and go when it closes. The two settings are the only things the extension stores, in the browser. Nothing is sent anywhere.

Status: alpha

What works today, on Twitch: the face-up cards on the table are outlined and named as they are played, with a hover card for each; cards stacked under others are remembered. The recognition runs in the browser, on WebGPU or, more slowly, on the processor.

Not yet: the plays panel and the legends and decklists that narrow the search (next, below), and YouTube (roadmap).

Known issues in 0.2.2: the rune count can be a rune or two off where runes are stacked tight, in a column or a fan, and the exhausted count is often short there. A card held still in a hand over the table can be read for a moment. A legend can be misread if you turn Wardeye on after its die is down. On a co-stream, what the streamer lays over the table (a webcam, a scoreboard) is known once the stream has cut away from the table a few times: before then a scoreboard's portraits can be read as cards, and Wardeye drops them, and withdraws their plays from the panel, once it knows. A part of a scoreboard that changes, such as its score track, can still be read as a card now and then.

Measured, not claimed

ResultMeasured on
Card detector v0finds 97.6% of the cards5,465 reviewed cards from three broadcasts: Los Angeles 98.1%, Barcelona 99.5%, Shenyang 96.2%. Trained on synthetic boards only (report)
Card identifier v1names 96.0% and 99.4% of the cardstwo broadcasts held out of training: Barcelona and the Los Angeles grand final (ml/README)
In the browserreads like the Python pipeline on 240 of 240 framesthe Los Angeles grand final, through the extension's own engine: the same cards, names and events

Next: plays, legends and decklists

The plays panel, the legend rule and pasted decklists are built and come with the next update of the extension, 0.2.2 (report).

  • The plays, beside the video. The list button on the badge opens a panel in the browser's side panel, as the local version has one beside its player.

    • Each player's legend, and what is face up on their side of the table.
    • The plays as they happened, newest first. Click one and the replay goes to just before it.
    • The list lives in the tab: nothing is stored, and it goes when the tab closes.
  • The table's layout as a guide. Battlefields are named in the strip along the middle of the table, where the official mat puts them, so a rune turned sideways elsewhere is not taken for one. Each player's runes are counted, with how many are exhausted, stacked runes included: every card is the same size, so a stack's step from strip to strip tells how many runes its gaps hide. The layout never drops a card: a unit that moves to a battlefield or changes hands keeps its name (D-028).

  • Graphics stay out. The table is found inside frames drawn between bars (Stockholm) and past a co-streamer's webcam laid over its border, and Riot's showdown banner is no longer read as cards.

  • Co-streams. What a co-streamer lays over every shot (their webcam and chat, a scoreboard, a sponsor banner) is found from the stream's cuts and never read. Only the table window tells the table camera from the other shots, so a close-up of a hand is not taken for it, and an arm over the mat does not take it away. On a wooden table the cards beside the wood are read.

  • The legends narrow the search, with nothing to set up.

    • Every card in a Riftbound deck must fit its legend's two domains, runes included.
    • Once Wardeye has read a player's legend, it compares a card on that player's half only with the cards the legend allows, about a third of the gallery.
    • On the hardest match measured, a Swiss round at Barcelona, the share of cards named right rises from 91.8% to 97.8%, and every confident read is right.
  • Paste the decklists, when they are published.

    • Paste each player's deck code, or a deckbuilder's export (text, tourney sheet or JSON), in that player's box in the plays panel, and Wardeye looks only among the cards on the list. The box says which legend the list names and any line it could not read.
    • A listed card counts in every printing, because players often use an alternate art or a reprint instead of the printing on the list. In the Barcelona final, 35% of the card sightings on the table were one of those.
    • Wardeye fetches no list from anywhere; it uses only what you paste.
  • A wrong list can't wreck it. A list is used only for the player whose legend it names; the other player stays on the legend rule. Applied blindly, another match's lists would name only 8.4% of the cards right.

  • Only for what is face up. A list only helps name the cards already on the table. Wardeye never shows it, and never uses it to guess a hand or a face-down card (D-026).

Private by design

  • It runs on your computer. Recognition happens in your browser. No video leaves your machine, and Wardeye keeps no history and has no account, analytics or ads.
  • Card data comes from Riot. Names and images load from Riot's public card gallery as you watch; Wardeye ships none of them.
  • Public information only. It reads only the table camera: never hand cams, face-down cards or the hand lists a broadcast shows.

Details: the privacy policy.

How it works

flowchart LR
  F[Video frame] --> R[Table window]
  R --> D[Card detector<br/>corners of each card]
  D --> E[Embed the art]
  E --> M[Match against the catalogue]
  M --> T[Track]
  T --> V[Events]
  V --> H[Hover overlay]
  V --> L[Timeline]

Cards are small on stream (roughly 70–140 px tall at 1080p) and their text is unreadable. So Wardeye identifies cards by art: a detector finds each card's corners, an embedder turns its picture into a fingerprint, and the fingerprint is matched against every printing of every card. Details in ARCHITECTURE.md and the research.

Principles

  1. Public information only. Hand cams, face-down cards and hidden deck contents are never processed. Wardeye sees only what the broadcast already shows, and a published decklist only helps name what is face up.
  2. Local first. Inference runs in the viewer's browser, on WebGPU with a WASM fallback. No video leaves the machine.
  3. Measured, not claimed. Every model comes with results on real broadcasts it was not trained on.
  4. Open and clean. AGPL code, permissively licensed dependencies and base models, no hidden telemetry. The trained weights ship inside the extension but are not published (D-022).
  5. No footage, card art or card text in Wardeye. Card data comes from Riot's public card gallery, in your browser. Broadcasts belong to their organisers.

For developers

npm install && npm run check          # typecheck, unit tests, guards
npm run test:e2e                      # browser tests, with stand-in models
cd ml && pip install -e '.[dev]' && pytest -q

The trained models are not in the repository (D-022): the store version carries them. A build from source has the whole extension but no models, and every test runs on stand-ins. The ml/ tools include a companion runner used in development (apps/extension). See CONTRIBUTING.md.

apps/extension     the Chrome and Edge extension: the overlay and the in-browser engine host
apps/bench         times the models in Chrome and checks them against PyTorch
apps/logger        timeline logger for ground-truth match logs
apps/reviewer      correct/wrong review of model guesses, turned into labels
apps/viewer        preview: hover the cards of a recorded match, with its timeline
packages/engine    the recognition pipeline in TypeScript, for the browser
packages/schema    timeline, catalogue and layout formats (Apache-2.0)
ml/                catalogue, stream simulator, training, evaluation and the live runner (Python)
assets/brand       the logo, colour tokens and fonts

Wardeye was called RiftEye until September 2026 (D-020); internal code names such as rifteye_ml and @rifteye/* still say so.

Documents

Brand bookLogo, colour, type, voice and how the product looks
Privacy policyWhat the extension reads, and what it never collects
Research breakdownGame model, vision pipeline, models and licences, data, delivery surfaces, prior art, licensing, legal, risks
ArchitectureComponents, data contracts, runtime topologies, sync, performance targets
RoadmapMilestones M0–M5 with measurable exit criteria
Decision logWhat is settled and why
ReleasingMaking the repository public; the Chrome Web Store release

Contributing

Contributions are welcome: bug reports with a timestamp on a public VOD, broadcast layout presets, design critique, prior art. See CONTRIBUTING.md. Contributors sign a CLA on their first pull request and keep the copyright to what they contribute. The project is free for everyone (D-022).

Licence

Wardeye was created under Riot Games' "Legal Jibber Jabber" policy using assets owned by Riot Games. Riot Games does not endorse or sponsor this project.

Wardeye isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing Riot Games properties. Riot Games, and all associated properties are trademarks or registered trademarks of Riot Games, Inc.

browser-extension
chrome-extension
computer-vision
onnx
twitch
webgpu

effe-exe/Wardeye

Place the ward. See the table.

TypeScript

3

120 commits

updated Oct 3, 2026

See the code

See what people are saying

SourceMessageScoreDate

Naming every face-up card on a TCG tournament stream, in the browser: a corner detector trained only on synthetic boards, a DINOv2 embedder, game rules as priors (r/computervision)

[Walktrough](https://reddit.com/link/1wwp43t/video/tufney77m9th1/player) Riftbound is Riot's trading card game, and its tournaments stream on Twitch from an overhead table camera. At 1080p a card is about 140 px tall, under dice, sleeves, hands and H.264, so viewers can rarely read the table.…

0

Oct 3, 2026

README

Wardeye

Place the ward. See the table.

A free browser extension for Riftbound streams on Twitch.
Alpha · AGPL-3.0 · a community project by Federico Vietti · effe-exe.github.io

Wardeye is the ward you place on a Riftbound stream. Point at any card on the table to see its name and official image, as the video plays, live or on replay. It works from the video alone, in your browser: no video leaves your computer.

It is made by Federico Vietti, who also makes Gradeon, the AI card pre-grading app. Wardeye shares Gradeon's dark look; it is not a Gradeon product.

How Wardeye works: a walkthrough in 1:21

The walkthrough (1:21, no sound) downloads from the 0.2.2 release. Footage: Riftbound Regional Qualifier Barcelona, grand final, official broadcast.

Riftbound Zone, the Italian Riftbound community, supports Wardeye. Read its article: Wardeye: the free extension that tells you which cards are on the table while you watch Riftbound on Twitch (also in Italian).

Install

Wardeye comes from the Chrome Web Store: Add Wardeye to Chrome. The store has version 0.1.2; 0.2.2, with the plays panel and the settings below, is next.

  • Chrome: open Wardeye's page in the Chrome Web Store and click Add to Chrome.
  • Edge: open the same page in Edge. Edge asks once to Allow extensions from other stores; allow it, then click Get.
  • Pin it (optional): click the puzzle-piece icon in the toolbar, then the pin next to Wardeye. Its toolbar button turns it off and on.
  • From GitHub, in Developer mode: each release has the extension's zip, the same package the store gets, often days before the store has it. Unzip it, open chrome://extensions (or edge://extensions), turn on Developer mode, click Load unpacked and choose the folder. It does not update itself: install the next release when it comes. The models inside are not open source (D-035).

You need desktop Chrome or Edge, version 137 or newer. A recent graphics chip (WebGPU) makes it fast; without one it runs on the processor, much more slowly. The second line of its badge says which.

From source? A build from this repository has the whole extension but not the trained models, which ship only inside the extension's package: the store version and the zip of each release (D-022). On its own it cannot read a table; see For developers.

Use it

  1. Open a Riftbound stream or replay on twitch.tv. Theatre mode and fullscreen work too.
  2. The Wardeye badge on the player says what it is doing: finding the table, then how many cards it has named.
  3. Point at a card on the table. The hover card shows its name, its official image, how sure Wardeye is, and what lies under it, such as gear on a unit.

On the video, each card Wardeye has named is marked at its corners, with its name above it; the card you point at is outlined. The badge shows the engine's timings when you point at it.

Turn it off in a tab with any of these; the toolbar button or Alt+R turns it back on:

  • the power button on its badge;
  • the Wardeye button in the toolbar (it says OFF while it is off);
  • Alt+R (Option+R on a Mac).

Off, the overlay is hidden and nothing is read.

The plays panel (from version 0.2.2): click the list button on the badge. A panel opens beside the page, with three tabs:

  • Match: each player's legend, their runes on the table (and how many of them are exhausted), the cards face up on their side, and the plays, newest first. Click a play and the replay goes back to just before it.
  • Decklists: a box for each player. Paste the player's published deck code or a deckbuilder's export (text, tourney sheet or JSON) and click Use this list. The box says which legend the list names and any line it could not read. A list counts only for the player whose legend it names, whichever box it is in, and it helps name only the cards face up on the table.
  • Settings:
    • On the video: Outlines and names (the default), Outlines only, or Clean, where nothing shows on the stream until you point at a card. Pointing at a card shows it in every view.
    • Performance: how often Wardeye reads the video. Full reads up to 5 frames a second, Balanced 2 and Light 1, for a computer busy with other apps; the lighter levels name cards more slowly.

The plays and the lists stay in the tab and go when it closes. The two settings are the only things the extension stores, in the browser. Nothing is sent anywhere.

Status: alpha

What works today, on Twitch: the face-up cards on the table are outlined and named as they are played, with a hover card for each; cards stacked under others are remembered. The recognition runs in the browser, on WebGPU or, more slowly, on the processor.

Not yet: the plays panel and the legends and decklists that narrow the search (next, below), and YouTube (roadmap).

Known issues in 0.2.2: the rune count can be a rune or two off where runes are stacked tight, in a column or a fan, and the exhausted count is often short there. A card held still in a hand over the table can be read for a moment. A legend can be misread if you turn Wardeye on after its die is down. On a co-stream, what the streamer lays over the table (a webcam, a scoreboard) is known once the stream has cut away from the table a few times: before then a scoreboard's portraits can be read as cards, and Wardeye drops them, and withdraws their plays from the panel, once it knows. A part of a scoreboard that changes, such as its score track, can still be read as a card now and then.

Measured, not claimed

ResultMeasured on
Card detector v0finds 97.6% of the cards5,465 reviewed cards from three broadcasts: Los Angeles 98.1%, Barcelona 99.5%, Shenyang 96.2%. Trained on synthetic boards only (report)
Card identifier v1names 96.0% and 99.4% of the cardstwo broadcasts held out of training: Barcelona and the Los Angeles grand final (ml/README)
In the browserreads like the Python pipeline on 240 of 240 framesthe Los Angeles grand final, through the extension's own engine: the same cards, names and events

Next: plays, legends and decklists

The plays panel, the legend rule and pasted decklists are built and come with the next update of the extension, 0.2.2 (report).

  • The plays, beside the video. The list button on the badge opens a panel in the browser's side panel, as the local version has one beside its player.

    • Each player's legend, and what is face up on their side of the table.
    • The plays as they happened, newest first. Click one and the replay goes to just before it.
    • The list lives in the tab: nothing is stored, and it goes when the tab closes.
  • The table's layout as a guide. Battlefields are named in the strip along the middle of the table, where the official mat puts them, so a rune turned sideways elsewhere is not taken for one. Each player's runes are counted, with how many are exhausted, stacked runes included: every card is the same size, so a stack's step from strip to strip tells how many runes its gaps hide. The layout never drops a card: a unit that moves to a battlefield or changes hands keeps its name (D-028).

  • Graphics stay out. The table is found inside frames drawn between bars (Stockholm) and past a co-streamer's webcam laid over its border, and Riot's showdown banner is no longer read as cards.

  • Co-streams. What a co-streamer lays over every shot (their webcam and chat, a scoreboard, a sponsor banner) is found from the stream's cuts and never read. Only the table window tells the table camera from the other shots, so a close-up of a hand is not taken for it, and an arm over the mat does not take it away. On a wooden table the cards beside the wood are read.

  • The legends narrow the search, with nothing to set up.

    • Every card in a Riftbound deck must fit its legend's two domains, runes included.
    • Once Wardeye has read a player's legend, it compares a card on that player's half only with the cards the legend allows, about a third of the gallery.
    • On the hardest match measured, a Swiss round at Barcelona, the share of cards named right rises from 91.8% to 97.8%, and every confident read is right.
  • Paste the decklists, when they are published.

    • Paste each player's deck code, or a deckbuilder's export (text, tourney sheet or JSON), in that player's box in the plays panel, and Wardeye looks only among the cards on the list. The box says which legend the list names and any line it could not read.
    • A listed card counts in every printing, because players often use an alternate art or a reprint instead of the printing on the list. In the Barcelona final, 35% of the card sightings on the table were one of those.
    • Wardeye fetches no list from anywhere; it uses only what you paste.
  • A wrong list can't wreck it. A list is used only for the player whose legend it names; the other player stays on the legend rule. Applied blindly, another match's lists would name only 8.4% of the cards right.

  • Only for what is face up. A list only helps name the cards already on the table. Wardeye never shows it, and never uses it to guess a hand or a face-down card (D-026).

Private by design

  • It runs on your computer. Recognition happens in your browser. No video leaves your machine, and Wardeye keeps no history and has no account, analytics or ads.
  • Card data comes from Riot. Names and images load from Riot's public card gallery as you watch; Wardeye ships none of them.
  • Public information only. It reads only the table camera: never hand cams, face-down cards or the hand lists a broadcast shows.

Details: the privacy policy.

How it works

flowchart LR
  F[Video frame] --> R[Table window]
  R --> D[Card detector<br/>corners of each card]
  D --> E[Embed the art]
  E --> M[Match against the catalogue]
  M --> T[Track]
  T --> V[Events]
  V --> H[Hover overlay]
  V --> L[Timeline]

Cards are small on stream (roughly 70–140 px tall at 1080p) and their text is unreadable. So Wardeye identifies cards by art: a detector finds each card's corners, an embedder turns its picture into a fingerprint, and the fingerprint is matched against every printing of every card. Details in ARCHITECTURE.md and the research.

Principles

  1. Public information only. Hand cams, face-down cards and hidden deck contents are never processed. Wardeye sees only what the broadcast already shows, and a published decklist only helps name what is face up.
  2. Local first. Inference runs in the viewer's browser, on WebGPU with a WASM fallback. No video leaves the machine.
  3. Measured, not claimed. Every model comes with results on real broadcasts it was not trained on.
  4. Open and clean. AGPL code, permissively licensed dependencies and base models, no hidden telemetry. The trained weights ship inside the extension but are not published (D-022).
  5. No footage, card art or card text in Wardeye. Card data comes from Riot's public card gallery, in your browser. Broadcasts belong to their organisers.

For developers

npm install && npm run check          # typecheck, unit tests, guards
npm run test:e2e                      # browser tests, with stand-in models
cd ml && pip install -e '.[dev]' && pytest -q

The trained models are not in the repository (D-022): the store version carries them. A build from source has the whole extension but no models, and every test runs on stand-ins. The ml/ tools include a companion runner used in development (apps/extension). See CONTRIBUTING.md.

apps/extension     the Chrome and Edge extension: the overlay and the in-browser engine host
apps/bench         times the models in Chrome and checks them against PyTorch
apps/logger        timeline logger for ground-truth match logs
apps/reviewer      correct/wrong review of model guesses, turned into labels
apps/viewer        preview: hover the cards of a recorded match, with its timeline
packages/engine    the recognition pipeline in TypeScript, for the browser
packages/schema    timeline, catalogue and layout formats (Apache-2.0)
ml/                catalogue, stream simulator, training, evaluation and the live runner (Python)
assets/brand       the logo, colour tokens and fonts

Wardeye was called RiftEye until September 2026 (D-020); internal code names such as rifteye_ml and @rifteye/* still say so.

Documents

Brand bookLogo, colour, type, voice and how the product looks
Privacy policyWhat the extension reads, and what it never collects
Research breakdownGame model, vision pipeline, models and licences, data, delivery surfaces, prior art, licensing, legal, risks
ArchitectureComponents, data contracts, runtime topologies, sync, performance targets
RoadmapMilestones M0–M5 with measurable exit criteria
Decision logWhat is settled and why
ReleasingMaking the repository public; the Chrome Web Store release

Contributing

Contributions are welcome: bug reports with a timestamp on a public VOD, broadcast layout presets, design critique, prior art. See CONTRIBUTING.md. Contributors sign a CLA on their first pull request and keep the copyright to what they contribute. The project is free for everyone (D-022).

Licence

Wardeye was created under Riot Games' "Legal Jibber Jabber" policy using assets owned by Riot Games. Riot Games does not endorse or sponsor this project.

Wardeye isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing Riot Games properties. Riot Games, and all associated properties are trademarks or registered trademarks of Riot Games, Inc.

browser-extension
chrome-extension
computer-vision
onnx
twitch
webgpu

Languages

TypeScript

52.9%

Python

38.6%

JavaScript

3.4%

CSS

2.6%

Shell

1.8%