TagTable
RFID video kiosk — place a tagged object on the reader, the right video plays.
TagTable is a fullscreen Windows kiosk app for museums, retail installs, and interactive exhibits. It listens to a USB HID RFID reader (EM4100/TK4100 125kHz tags) and plays a mapped 4K video the moment a tagged object lands on the table. Hardware-accelerated playback, an admin panel behind Ctrl+Shift+A for tag→video mapping, customizable idle logo, optional DMX lighting cues, and a mock mode for development without the reader connected.
- JavaScript
- Electron 33
- Node.js
- serialport
- osc-js
- electron-builder
- Inno Setup
- Rust
- egui
TagTable is a fullscreen Windows kiosk app for tag-driven video — place an RFID-chipped object on the reader and the corresponding 4K clip starts playing. Built for museums, retail showcases, and interactive exhibits where the interaction model is "pick something up, see what it tells you." Hardware-accelerated playback, near-instant tag-to-video latency, admin panel behind Ctrl+Shift+A, mock mode for development without the reader plugged in.
What it actually does
- RFID tag detection via USB HID — talks to any EM4100 / TK4100 125kHz reader that enumerates as a keyboard. Tag UID arrives, the engine looks up the mapped video, the kiosk swaps to it. No server round-trip; the whole map lives on disk.
- Hardware-accelerated 4K video. The Rust prototype used libmpv direct; the current Electron build leans on Chromium's built-in decoder pipeline. Either way: no external player, no second window, no flash of black.
- Admin panel at Ctrl+Shift+A with three modes: Assign (scan a tag, pick a video, map it), Manage (review and delete existing mappings), Settings (idle logo, playback options, license, DMX cues). Tab cycles between modes; ESC drops back to idle.
- DMX lighting integration (optional) — fire a configured cue when each tag triggers, so the room itself responds to what the visitor picked up. OSC-out the side for any other show-control gear.
- Mock mode for development — start with
--mockand the app fakes scans on a hotkey. No reader required to iterate on layout or video pacing. - Update + license checks at startup with an offline grace window, so a kiosk on a spotty network doesn't fall over the first time the wifi drops.
Two implementations, same product
TagTable shipped first as a Rust + egui
desktop binary cross-compiled from Linux to Windows via MinGW
+ FFmpeg shared libs. That version still lives in the repo
under rust-legacy/ and the README documents it.
The current production build is a JavaScript + Electron
rewrite (v2.x) that traded the leaner footprint of the Rust
version for faster iteration on the admin UI and better Mac
support — same RFID flow, same video path, same admin gestures.
The README's "Built with Rust" framing is accurate for the
legacy code; the live installer is Electron.
Why the table
Most "interactive" exhibits are screens with buttons. TagTable's pitch is: the screen is incidental — the artifact you're holding is the input. Tagging objects with cheap 125kHz cards and letting curators map clips through a hotkey panel makes the "swap the experience next month" loop quick enough that the exhibit team can iterate without involving a developer.
Straight from the source
The project's own README.
Rendered in place — every link, image, and code block carried over from the repo. The page below is what a contributor would see opening the project for the first time.
TagTable - RFID Video Kiosk
A Windows-based video kiosk application that plays videos when RFID-tagged objects are placed on a reader. Built with Rust and egui for seamless, instant video playback.
Features
- RFID Tag Detection: Interfaces with USB HID RFID readers (EM4100/TK4100 125kHz tags)
- Instant Video Playback: Hardware-accelerated 4K video via libmpv - no external player needed
- Admin Mode: Press
Ctrl+Shift+Ato access the admin interface- Assign Mode: Map RFID tags to videos
- Manage Mode: View and delete existing mappings
- Settings Mode: Configure videos, logo, playback, and system settings
- Customizable Logo: Display your own logo when idle
- Update Notifications: Checks for updates on startup
- Product Key Licensing: Online license validation with offline grace period
- Mock Mode: Test the application without RFID hardware
Quick Start
Installation (Windows)
- Download the latest release from tagtable.download
- Run
TagTable-X.X.X-Setup.exe - Launch TagTable from the Start Menu
See docs/INSTALL.md for detailed installation instructions.
Basic Usage
- Launch TagTable - Starts in fullscreen mode
- Add Videos - Place video files in the
videos/folder - Enter Admin Mode - Press
Ctrl+Shift+A - Assign Tags - Select a video, then scan a tag
- Press Tab - Switch between Assign/Manage/Settings modes
- Exit - Press
ESCto return to normal mode
See docs/USAGE.md for the complete user guide.
Technical Overview
Architecture
TagTable uses a state machine architecture with three primary states:
┌─────────────────────────────────────┐
│ │
▼ │
┌──────────┐ RFID Scan ┌───────────┐ Video Ends │
│ IDLE │ ─────────────► │ PLAYING │ ───────────────┘
│ (Logo) │ (mapped tag) │ (Video) │
└──────────┘ └───────────┘
│
│ Ctrl+Shift+A
▼
┌──────────────────────────────────────────┐
│ ASSIGNMENT (Admin) │
│ ┌────────┐ ┌────────┐ ┌──────────┐ │
│ │ Assign │ │ Manage │ │ Settings │ │
│ └────────┘ └────────┘ └──────────┘ │
│ (Tab key cycles modes) │
└──────────────────────────────────────────┘
Tech Stack
| Component | Technology | Purpose |
|---|---|---|
| Language | Rust 2021 Edition | Systems programming with memory safety |
| GUI Framework | egui 0.29 + eframe | Immediate-mode GUI, cross-platform |
| Video Playback | libmpv2 | Hardware-accelerated 4K playback |
| Serialization | serde + serde_json | Configuration and mapping persistence |
| HTTP Client | ureq | Update checking |
| CLI Parsing | clap 4.0 | Command-line arguments |
| File Dialogs | rfd | Native folder/file selection |
| OpenGL | glow | GPU rendering for video frames |
Module Structure
src/
├── main.rs # Entry point, CLI args, event loop, app initialization
├── state.rs # State machine: AppState, AdminMode, AdminState
├── ui.rs # UI rendering: idle screen, admin panels, settings
├── config.rs # Configuration: Config, Mapping, PlaybackConfig
├── player.rs # Video playback: libmpv hardware-accelerated player
├── version.rs # Version info and update checking
├── license.rs # Product key licensing with online validation
├── assets.rs # Embedded assets (default logo)
├── paths.rs # Path resolution and data directories
└── input/
├── mod.rs # Input handler interface
├── rfid.rs # RFID tag detection (10-digit buffer)
└── admin_trigger.rs # Ctrl+Shift+A detection
Key Concepts
Immediate Mode GUI (egui)
- UI is rebuilt every frame based on current state
- No retained widget state - simple, predictable rendering
- Ideal for kiosk applications with simple UI needs
RFID as HID Keyboard
- RFID readers emulate USB keyboards
- Output: 10 decimal digits + Enter key
- Application captures keystrokes and buffers them
- Timeout-based detection (50ms default)
Hardware-Accelerated Video Playback
- libmpv with OpenGL render API
- Hardware decoding: D3D11VA (Windows), VAAPI (Linux), V4L2 (Pi)
- Smooth 4K playback on modest hardware
- Supports MP4, MOV, AVI, MKV, WebM
JSON Configuration
config.json: Application settings (video directory, logo, playback)mapping.json: Tag-to-video associations- Both auto-created with defaults if missing
Configuration
config.json
{
"video_directory": "./videos",
"input_timeout_ms": 50,
"playback_cooldown_ms": 2000,
"admin_code_enabled": true,
"fullscreen": true,
"mock_mode": false,
"logo": {
"path": "./logo.png",
"display_mode": "centered",
"opacity": 1.0
},
"playback": {
"volume": 1.0,
"loop_video": false,
"scale_mode": "fit",
"end_behavior": "return_to_idle",
"fade_duration_ms": 2000
}
}
| Field | Type | Description |
|---|---|---|
video_directory |
path | Directory containing video files |
input_timeout_ms |
number | RFID digit buffer timeout (ms) |
playback_cooldown_ms |
number | Debounce period between same tag scans |
admin_code_enabled |
bool | Enable Ctrl+Shift+A admin access |
fullscreen |
bool | Start in fullscreen mode |
mock_mode |
bool | Enable mock mode (single-digit tags) |
logo.path |
path | Custom logo image path |
logo.display_mode |
string | centered, fit, stretched, or tiled |
logo.opacity |
float | Logo transparency (0.0 - 1.0) |
playback.volume |
float | Audio volume (0.0 - 1.0) |
playback.loop_video |
bool | Loop videos when they end |
playback.scale_mode |
string | fit, fill, stretch, or native |
playback.end_behavior |
string | return_to_idle, fade_to_idle, or hold_last_frame |
mapping.json
{
"0008265812": "intro_video.mp4",
"0009912345": "product_demo.mp4"
}
Simple key-value mapping of 10-digit RFID tag IDs to video filenames.
Requirements
Target System (Windows)
- Windows 10/11 (64-bit)
- USB RFID Reader (125kHz, HID keyboard emulation)
- Video files in supported formats (MP4, MOV, AVI, MKV, WebM)
Development (WSL2/Linux)
- Rust 1.70+
- mingw-w64 (for cross-compilation to Windows)
- just (command runner)
Development
Setup
# Install cross-compilation tools
just setup
# Or manually:
rustup target add x86_64-pc-windows-gnu
sudo apt-get install -y mingw-w64
Building
# Build Windows release binary
just build-win
# Build and bundle into dist/
just bundle
# Run in mock mode (test without hardware)
just run-mock
# Run locally in windowed mode
just run
# Build Windows installer (requires Inno Setup)
just installer
Build System
The project uses just as a command runner. Key recipes:
| Command | Description |
|---|---|
just setup |
Install cross-compilation toolchain |
just build-win |
Build Windows release binary |
just bundle |
Create dist/ folder with exe + config + videos |
just run-mock |
Run locally in mock mode (windowed) |
just run |
Run locally in windowed mode |
just test |
Run Rust tests |
just lint |
Run clippy with strict warnings |
just fmt |
Format code with rustfmt |
just ci |
Full CI pipeline (fmt + lint + test + build) |
just installer |
Create Windows .exe installer |
Cross-Compilation
TagTable is developed on Linux/WSL2 and cross-compiled for Windows:
- Host: Linux x86_64
- Target: x86_64-pc-windows-gnu
- Toolchain: MinGW-w64 (GNU toolchain for Windows)
The .cargo/config.toml configures the Windows linker automatically.
Project Structure
tagtable/
├── Cargo.toml # Dependencies and project config
├── Cargo.lock # Locked dependency versions
├── config.json # Runtime configuration
├── mapping.json # Tag-to-video mappings
├── justfile # Build commands
├── videos/ # Video files directory
├── assets/ # Logo and fonts
│ ├── tag-table-logo.png # Default embedded logo
│ └── JetBrainsMono-Regular.ttf # UI font
├── installer/ # Inno Setup scripts
│ └── tagtable.iss
├── docs/ # Documentation
│ ├── CHANGELOG.md
│ ├── INSTALL.md
│ ├── USAGE.md
│ ├── UPDATE_ENDPOINT.md
│ └── server/ # Server-side deployment docs
│ ├── README.md
│ ├── UPDATE_ENDPOINT.md
│ ├── DIRECTORY_STRUCTURE.md
│ └── DEPLOYMENT.md
└── src/ # Rust source code
├── main.rs
├── state.rs
├── ui.rs
├── config.rs
├── player.rs
├── version.rs
├── license.rs
├── assets.rs
├── paths.rs
├── dmx/ # DMX lighting control (Art-Net, sACN, Serial)
└── input/
├── mod.rs
├── rfid.rs
└── admin_trigger.rs
Keyboard Shortcuts
| Key | Context | Action |
|---|---|---|
Ctrl+Shift+A |
Idle | Enter Admin Mode |
Ctrl+Q |
Any | Force quit |
ESC |
Idle | Quit application |
ESC |
Admin | Exit Admin Mode |
Tab |
Admin | Cycle mode: Assign → Manage → Settings |
Arrow Up/Down |
Admin | Navigate lists/tabs |
Enter |
Settings | Select tab |
Delete |
Manage | Remove selected mapping |
Hardware
Supported RFID Readers
- YARONGTECH 125kHz USB RFID Reader
- FissaiD or similar HID keyboard-emulating readers
- Any reader that outputs 10 digits + Enter
Note: Choose readers without audible beeps for seamless user experience.
Supported Tags
- EM4100/TK4100 125kHz Read-Only Passive Tags
- Tags output a 10-digit decimal UID
Documentation
- Installation Guide
- User Guide
- Changelog
- Update Endpoint Specification
- Server Deployment - Setting up the update server on tagtable.download
Deployment
Releasing a New Version
- Update version in
Cargo.toml - Build and bundle:
just ci && just bundle - Build installer:
just installer - Deploy to server:
just deploy - The version endpoint is updated automatically
See docs/server/DEPLOYMENT.md for full workflow.
License
MIT
Build something like this
Want a tool like this for your shop?
We've shipped this kind of thing before. Twenty-minute intro call, no slides.