I'm th108 — a free companion app for the Epomaker TH108 V2 PRO keyboard. I run on your PC and drive its per-key RGB and LCD screen with effects the keyboard's own firmware can't do on its own:
Unofficial & not affiliated with Epomaker. See the Docs tab for a full feature guide, comparisons and legal notes.
While connected, click this page once so it has keyboard focus, then type — pressed keys flash orange.
⚠ Browser keydown only fires while this tab is focused. Reactive-in-any-app comes from the background daemon instead.
💡 Daemon running? Skip Connect for layer editing — edits push to it live and reactive keeps working in every app. Connect is only for the GIF→keys, onboard-effect, and LCD tools (it takes the keyboard from the daemon until you close this tab).
Runs your layers always-on — reactive typing in any app — and takes over whenever this page is closed.
One process name per line or comma-separated — the name in Task Manager → Details (e.g. obs64, steam). While that app is in front, a lighting freeze clears in ~12 s instead of ~30 s. Can't stop the freeze, only speed the recovery.
Drag any card by its title bar to rearrange the grid — the layout is saved.
Tick the tabs whose card arrangement should go back to default. Settings and content are untouched — this only moves cards.
That white NumLock LED is forced on by the firmware whenever NumLock is ON — it can't be painted over (proven at 30 fps) and Epomaker's own software has no setting to disable it. The only fix is to keep NumLock off, so this makes the numpad still type numbers in that state:
Click a light — your Spacebar becomes its cycle key and a focus overlay opens; Esc or ✕ restores the Spacebar.
Give one key a smarter behavior. Like everything on this tab it lives
in the keyboard's keymap — works in every app, no software needed. Pick a key on the board, choose a behavior,
Apply. Restore Default undoes it. (The official tool allows up to 40 advanced keys.)
⚠ Heads-up — not working on this board's firmware: these entries write correctly (the official Epomaker
tool even reads them back and shows them identically), but the board's firmware doesn't execute the
advanced-key types — and the official tool's own advanced keys don't fire either (verified 2026-06-30 against a
full byte-for-byte capture of its connect + apply). So it's a firmware limitation, not a software gap; if a
firmware update ever adds support, these bindings will just start working. Normal remaps + light functions on
this tab are unaffected.
Snapshot your whole lighting setup — layers, pattern settings, stacking order — as a named profile. Apply, rename, export, or import them; up to 10. Click a name to rename it.
The full Lighting-tab setup: all your layers with their pattern settings, plus the stacking order — the same data the background daemon runs. Key bindings live in the keyboard's own flash, so they stay put across profile switches. Exported files are daemon-compatible JSON.
The daemon — a small background app that drives your keyboard's lighting when this page is closed or unfocused. It's what lets reactive lighting and effects work in every app, not just this browser tab.
Driver (page vs daemon) — only one thing paints the LEDs at a time: either this tab (over WebHID) or the daemon. "Who's driving" is just which of the two is currently in control.
Lease / yield · auto-yield — the handoff of the keyboard between the tab and the daemon. Click away and the tab yields to the daemon; click back and it reclaims. Auto-yield is this happening automatically on focus changes (you can turn it off with "Keep driving from this tab").
Reactive — a layer type where keys light up in response to keypresses. It only sees keys typed into the focused app of whoever's driving — the tab sees only this tab; the daemon sees the whole system.
Churn — rapid back-and-forth handoffs of the keyboard between tab and daemon (e.g. from quickly alt-tabbing). Excess churn can desync the keyboard's USB command stream.
Mute / wedge — when the keyboard stops accepting lighting updates (its USB command queue desyncs) so the LEDs freeze or go dark. It usually self-recovers in ~30–45s via a USB restart; toggling wired ↔ Bluetooth or replugging forces an instant fix.
Onboard effect — the keyboard's own built-in lighting (rainbow, etc.) stored in its firmware. It runs when nothing is driving from the host; the daemon or page paints over it.
It hands the keyboard from the background daemon to this page and starts your layers automatically — one click, no second step. You only need it for the GIF→keys player and the LCD uploader; plain layer editing pushes to the daemon live without ever connecting.
This only concerns layers whose type is Reactive (whichever level they sit at — other layer types aren't affected). Once this page owns the keyboard (after Connect), it can only see keystrokes typed into this tab — a browser limit. Reactive-in-every-app comes from the background daemon: close this tab (or never Connect) and it takes over within a second.
Host Actions (Hotkeys → Assign to Key → Host Actions tab) bind a key to a background-app action — Mic/Music lighting on-off, or Profile Next/Prev. They run through the daemon, so they work with this page closed. Click the action, then press the key you want to use — it captures whatever that key sends, so it works even on a firmware-remapped key (and the key still does its normal job too).
Want a key to do only the host action and not its normal job — e.g. the Menu key, which otherwise
opens the right-click menu? First firmware-remap that key to a do-nothing key: Pause/Break is ideal — it
does nothing in Windows and has no indicator light. Then bind the host action by pressing that key (it'll show
up as Pause, which is correct — that's what the key now sends). Avoid Scroll Lock and the
other lock keys: the firmware forces their indicator LED on (white), which you can't paint over.
The Agent layer can light up for one specific Claude Code session instead of all of them. Tick 🎯 Follow focused session (Agent layer → Session) and it automatically tracks whichever Claude session's VSCode window is in front — bring a different window forward and the lighting follows it.
It works on its own, no extra tools: the background app watches the Windows foreground window and matches it to a session by project folder (the window title carries it). Leave all VSCode windows — switch to a browser, say — and after a few seconds it reverts to All Sessions. Windows only; on other platforms Follow-focus simply stays on All Sessions.
Granularity — it follows by PROJECT, not by tab. Focus is matched from the VSCode window, which only identifies the project folder. So switching between Claude tabs/sessions inside the same project won't change what's followed (and won't re-flash) — if several live sessions share a project, it follows the busiest one. Per-tab following isn't possible from the window alone: two sessions in one window share it, and the Claude hooks give the daemon only a session id + folder, no per-session title to tell tabs apart.
Advanced: anything can also drive it directly by POSTing { session_id } to
http://localhost:8123/agent/focus (null clears it) — but you don't need to.
They're hidden because this keyboard's firmware doesn't run them — not a software issue on our end. Advanced keys are stored in the keyboard's keymap, and we write them correctly: the official Epomaker tool even reads our bindings back and displays them identically, which confirms the encoding is exactly right. But the firmware never executes those entry types, so the key just goes dead.
We verified this against the official driver byte-for-byte (2026-06-30): its setup sends no special "enable" command, its writes are identical to ours, and its own advanced keys don't work on this board either — a Combination set in the official tool doesn't fire in its "Test Your Bindings." So it's a firmware limitation shared by both tools. Normal key remaps and the light-function binds on the Hotkeys tab work fine — only these four advanced types are affected. If a firmware update ever adds support, the feature can be switched back on and the existing encoding will just work.
The one-click Numpad → Digits fix on the Hotkeys tab kills the white NumLock LED by remapping the numpad to top-row digits in firmware — but that's global, so apps that read the real numpad (Blender's camera on Numpad 1/3/7, some CAD/DAW shortcuts) stop working. Firmware can't be per-app; a small host-side script can. It keeps the numpad native and turns it into digits everywhere except the apps you list.
You'll need AutoHotkey v1.1 (free).
Then: 1) on the Hotkeys tab click Revert Numpad (back to native). 2) Turn NumLock off
(LED off). 3) Save the script below as numpad-apps.ahk and double-click it — a green
H appears in your tray (check the hidden-icons ^ arrow). To run it every login, drop a shortcut to
it in your shell:startup folder.
#NoEnv
#SingleInstance Force
; Numpad types DIGITS everywhere EXCEPT the apps listed below, where it passes
; through native (so Blender camera / CAD numpad shortcuts still work).
#If !(WinActive("ahk_exe blender.exe"))
NumpadEnd::SendInput 1
NumpadDown::SendInput 2
NumpadPgDn::SendInput 3
NumpadLeft::SendInput 4
NumpadClear::SendInput 5
NumpadRight::SendInput 6
NumpadHome::SendInput 7
NumpadUp::SendInput 8
NumpadPgUp::SendInput 9
NumpadIns::SendInput 0
NumpadDel::SendInput {.}
#If
To cover more apps, extend the WinActive(...) line, e.g.
#If !(WinActive("ahk_exe blender.exe") or WinActive("ahk_exe Fusion360.exe")). It assumes the app
reads the numpad with NumLock off (Blender does by default; if a key misbehaves, turn on that app's own
"emulate numpad" option). It's a normal AutoHotkey process — kill it anytime from its tray icon, and it's
fully independent of the lighting daemon.
One caveat — this keeps Blender's numpad native, which is what makes the camera (Numpad 1/3/7) work, but it means typing digits into a value field (bone angle, a transform amount) still needs NumLock on. No per-app trick can make the same keys do camera and digit-entry automatically. The simplest fix is to just flip NumLock on for your Blender session — nav and field digits both work natively — and off when you're done; the white LED is lit only while you're in Blender.
After the one-click Numpad → Digits remap, the numpad types numbers in normal apps but in PowerShell, Command Prompt, and Windows Terminal the numpad keys act as if NumLock is off — you get Home / End / PgUp / PgDn / arrows instead of digits. That's a Windows console quirk, not a bug in the remap: the console reads numpad keys by their physical scancode and NumLock state at a lower level than the firmware remap reaches, so it ignores the remap and applies its own NumLock-off navigation.
Fixes, easiest first: 1) use the top number row when you're in a terminal; 2) turn
NumLock on just for terminal work (the numpad types digits again — but the white NumLock LED comes back
while it's on); or 3) use the AutoHotkey method in the card above — its SendInput
digits are accepted by the console, so with NumLock off the numpad types numbers in PowerShell too, with no
white light. (The daemon can't fix this: it paints lighting, it doesn't inject keystrokes.)
With the daemon running, every layer edit pushes to it live (~½ s) and reactive keeps working everywhere. Connect is only for the GIF→keys player, onboard effects, and the LCD uploader.
The board sometimes goes mute — it silently stops accepting host lighting (typing still works) and falls back to its saved firmware effect, which usually looks static. USB power events are the main trigger: sleep, or other devices power-cycling on the same bus. Rapidly handing the board back and forth between this tab and the background daemon — lots of Connect/Disconnect, replug, or tab-switching in quick succession — can also wedge it: every handoff briefly risks two writers on the same board, and its firmware doesn't love the churn. If you're toggling a lot, give it a second to settle between switches.
It can also go mute on its own during normal use — no sleep, no replug, nothing you did. Streaming per-frame lighting for a long stretch (heavy reactive typing, or an audio/media layer running for hours) can make the firmware occasionally choke on its own write queue and stop acknowledging frames. This is a known hardware quirk of the board, not a fault in the app — which is why the daemon watches for it and recovers automatically (a USB restart), usually within a minute, without you touching anything. If your lighting "fixed itself," that's what happened.
Cure ladder: wait ~30 s (automatic USB restart; some mutes self-recover) → press Ctrl+Alt+End anywhere (the manual recovery hotkey, same restart) → flip the mode switch to BT and back to wired → ⟳ Factory Reset Keyboard (Home tab; the reset command gets through even when lighting is dead — then Restore Keymap on Hotkeys brings your binds back). Note the board runs on battery, so unplugging the cable alone does NOT reboot it — deep wedges sail straight through replugs and USB restarts. To make mutes rarer in the first place, see the "Stop Windows from suspending the keyboard" card.
Out of the box, Windows is allowed to power-manage the keyboard's USB interfaces — and a USB suspend is exactly what puts the board into its mute state. A one-time fix removes that permission.
Right-click th108-daemon\disable-usb-powersave.ps1 → Run with PowerShell (it asks for
admin), then replug the keyboard once. It clears the per-interface "allow the computer to turn off this
device" flags and turns off USB selective suspend in your power plan. Run it again with -Enable
to undo both.
Three indicators, three different things. The header Online tag = the background daemon process is running. The Background Daemon card says whether it currently holds the keyboard. The Keyboard card's status is whether this page holds it (Connect). They can legitimately disagree — e.g. daemon Online and driving the keyboard while the page shows "not connected" is the normal resting state.
That's Windows, not this suite. The media keys go to the most-recently-active media session, and Chrome/Edge grab it aggressively — so your keyboard's ⏯ hits a browser tab instead of Spotify. The clean fix is to stop the browser from claiming the keys:
Chrome: chrome://flags/#hardware-media-key-handling → Disabled → Relaunch.
Edge: edge://flags/#hardware-media-key-handling → Disabled → Relaunch.
Firefox: about:config → media.hardwaremediakeys.enabled → false.
After that the media keys go to Spotify (or whatever non-browser app is playing). Trade-off: that browser then ignores the media keys entirely — which is the point.
Epomaker's official web driver lives at epomaker.driveall.cn — useful for the firmware features this suite doesn't cover yet. Two warnings: quit the background daemon first (Home tab) — only one controller can hold the keyboard, otherwise the official site errors out on connect — and it has been seen scrambling keymaps when changing bindings, so take a Backup Keymap on the Hotkeys tab before using it.
Both buttons live in the Toolbox card on the Home page.
↺ Reset All Settings resets this page: layers, per-pattern values, LCD calibration, and panel layout. Kept: your media Library, saved Profiles (delete those individually on the Profiles tab), and the keymap backup + remapped-key marks — those mirror the keyboard itself, which this reset never touches.
⟳ Factory Reset Keyboard is the opposite: it resets the keyboard's hardware — onboard config and keymap, including your key bindings — and changes nothing on this page.
Both put a GIF on your keys, but they work differently. A Media layer plays the GIF inside the Layer Compositor, so it blends with your other layers — opacity, blend mode and the global Adjust all apply — and it runs through the background app, so it keeps going with this tab closed. The GIF → Keyboard Lighting card on the Lighting tab plays a GIF on its own, taking over the whole board, and needs this page connected. Use the Media layer to mix a GIF into a stack; use the card for a quick full-board GIF.
Everything th108 adds on top of your TH108 V2 PRO, by area. Most of it is host-driven — your PC computes the lighting frame-by-frame and streams it to the board, so effects can react to things the keyboard can't see on its own (what you type, what's playing, what's on screen).
A Reactive layer lights each key as you press it — system-wide, in any program — because the always-on background app sees your keystrokes. (A browser tab can only see keys typed into itself, so this comes from the daemon, not the page.)
Map any GIF onto the per-key grid and play it across the keyboard. Requires Connect (this page drives the board for the GIF player).
Write the keyboard's built-in firmware effects from the page — useful as a clean baseline, or to overwrite a corrupt onboard config.
Upload images and GIFs to the keyboard's little display, plus a Now Playing readout. (Uploads are chunked and rate-limited for hardware safety.)
Save and switch whole setups (layers + lighting + binds). Export/Import to JSON to back up or move between machines.
The always-on engine: keeps your layers and reactive typing running in every app and takes over whenever this page is closed. Options: auto-start at login, auto-fix lighting wedge (USB restart), and lights-off when the monitor is off.
Some programs — screen recorders and game launchers/overlays like OBS or Steam — probe the keyboard in a way that can briefly freeze the lighting (a "wedge"). List their process names in the Apps that grab the keyboard box on the daemon panel, and while one of them is the active window the background app clears a wedge fast (~12s) instead of waiting out the normal typing-safe delay (~30s). It can't stop the freeze — that's the other app touching the hardware — only recover from it sooner, and it never restarts the USB mid-keystroke. Find process names in Task Manager → Details.
The dropdown in the top bar switches the interface between 18 languages, including right-to-left layouts for Arabic. Your choice is remembered on this device.
Epomaker ships an official browser configurator (the WebHID page) for the TH108. It writes settings into the keyboard's onboard memory, then your PC steps out of the way. th108 is different in kind: it's a host-driven suite — your PC keeps computing and streaming the lighting, which is what makes the reactive / audio / live-composite effects possible. The two can't drive the board at the same time.
| Capability | Epomaker WebHID | th108 |
|---|---|---|
| Where effects run | On the keyboard (onboard) | On your PC, streamed live |
| Works after the tab closes | Yes (onboard) | Yes — via the background daemon |
| Multi-layer live compositing | One onboard effect (Musical Rhythm layers 2, fixed) | Up to 8 freely-blended layers |
| Reactive typing in every app | Onboard reactive only | Yes (daemon sees system-wide keys) |
| Audio-reactive lighting | Onboard “Musical Rhythm” only | System / app / tab / mic — as a blendable layer |
| GIF → keys | No | Yes |
| Bind keys to PC actions | No | Host Actions (apps, windows, macros…) |
| LCD image / GIF upload | Yes | Yes + Now Playing |
| Onboard effect editing | Yes | Yes |
| Profiles / export-import | Onboard slots | Host profiles + JSON |
| Always-on PC background app | Not needed / none | Yes (optional, auto-start) |
Comparison covers the official tool's per-key lighting configurator and its onboard “Musical Rhythm” audio effect; Epomaker may add features over time. Use whichever fits — onboard effects from either tool survive a reboot; host-driven effects need th108 running.
The web page alone drives your keyboard over WebHID while this tab is open — great for a quick try. The optional companion daemon unlocks the rest: always-on lighting (keeps running with the page closed), the Audio and Agent layers, Now-Playing on the LCD, and Host Actions.
setup.cmd from the top of that unzipped folder — it installs dependencies, enables start-at-login (per-user, no admin), and opens the controller at http://localhost:8123/.Windows only for now. The daemon is a small local Node process — nothing is uploaded anywhere. Once it’s running you open http://localhost:8123/ and this prompt no longer appears.
Notable additions. Newer entries land on top as the suite updates.
th108 is an independent, unofficial, free tool. It is not affiliated with, endorsed by, sponsored by, or supported by Epomaker. "Epomaker" and "TH108" are trademarks of their respective owners and are used here only to identify the compatible hardware (nominative use).
Provided "as is", without warranty of any kind. It communicates with hardware over standard HID lighting and screen commands; you use it at your own risk. The authors are not liable for any damage, data loss, or device malfunction. It does not flash or modify keyboard firmware.
Everything runs locally on your machine. There is no telemetry and no data collection. Audio, tab, and microphone capture used for reactive effects stay on your PC and are never uploaded.
Onboard-effect tooling adapts open-source work (e.g. OpenRGB protocol notes). Respect the licences of any such components when redistributing.
Firmware effects — run on the keyboard itself, persist without a PC. Setting one stops the live layers/GIF.
The background daemon shows the current song on the LCD with album art (Spotify only by default). ⚠ Each update writes the LCD flash, which can occasionally softbrick this board. To minimize that: it updates only on a track change (never play/pause/resume), waits ≥20 s between writes, and caps at 30/hour. Still a real risk — leave it off if you can't afford a possible factory reset. While this page holds the keyboard, a new song queues and lands when the daemon takes over.
⌨ Heads-up: during each LCD upload the key lighting pauses and the board can briefly drop input (a fraction of a second) — not recommended during gaming or competitive play.
⚠ More impact than the bar alone: each song update flashes the keyboard and briefly drops input. The bar below is lighting-only and has no such cost.
Sets the keyboard's onboard effect to black, so when the board briefly falls back during an update it shows a dark blink instead of the rainbow startup. Best-effort — a true hardware re-init can still flash; A/B it on vs off. Writes a firmware setting + cycles the keyboard once when toggled.
Keys 1-0 light up to show how far through the song you are — lighting only, no LCD writes, so it can't softbrick. Needs the background daemon running (it reads Spotify's playback position). Works whether or not the LCD now-playing on the left is on.