RGB control for ASUS Aura motherboards that does not need Armoury Crate, does not install a driver, and does not put an executable on disk.
Built on a PRIME A620AM-K with Smart App Control enforcing. Prism talks to the Aura controller over the HID stack Windows already ships, so there is nothing to install, nothing for Smart App Control to block, and nothing to go stale on an update.
Armoury Crate runs fifteen processes to change the colour of some fans, and it breaks. Prism does the same job from a single process that you can close, because most of the time the lighting is not running in Prism at all — it is running on the controller.
Aura controller VID_0B05 PID_1BED firmware AULA3-AR36-0104
Transport USB HID, usage page 0xFF72, 65-byte reports
Channels 3 addressable ARGB headers, up to 120 LEDs each
Onboard LEDs none (config_table[0x1B] = 0)
Prism reads the controller's config table at startup, so it adapts to whatever board it finds rather than hard-coding this one.
Double-click Prism.vbs.
That is the whole thing. wscript.exe starts powershell.exe, which compiles src\*.cs into
memory using the C# compiler already inside Windows and runs the app in-process. No .exe is
written, no SDK is needed, nothing is installed. Startup costs about a second.
powershell -ExecutionPolicy Bypass -File .\Prism.ps1 does the same with a console window, which
is useful when something has gone wrong.
Settings live in %APPDATA%\Prism\config.json.
Smart App Control is on and enforcing here (VerifiedAndReputablePolicyState = 1,
UsermodeCodeIntegrityPolicyEnforcementStatus = 2). It blocks unsigned locally-built binaries on
sight, there is no per-app exception, and self-signing does not help — it trusts Microsoft's
certificate list and reputation service, not your certificate store.
It does not block powershell.exe, and it is not forcing PowerShell into Constrained Language
Mode here ($ExecutionContext.SessionState.LanguageMode reports FullLanguage, and Add-Type
works). So the in-memory route produces the identical app with nothing for the policy to reject.
Turning Smart App Control off is one-way — it cannot be switched back on without resetting Windows. There is no reason to spend that here.
build.ps1 will still produce bin\Prism.exe if your machine has no such policy.
Prism registers a shortcut in the Startup folder, not an HKCU\...\Run key.
Both launch reliably, but Windows' own startup enumeration — Task Manager's Startup apps tab and Settings → Apps → Startup — did not list the Run entry when its command was a script host, so there was no way to see or turn it off from the UI. A Startup shortcut shows up by name:
Name Command Location
Prism Prism.lnk Startup
The shortcut is re-pointed at every launch, so moving the Prism folder does not leave a dead entry behind. Start hidden at login only affects that automatic launch — opening Prism yourself always shows the window.
Every effect is either on the controller or rendered by Prism, and the UI labels which.
| Controller effects | Prism effects | |
|---|---|---|
| Runs on | The Aura chip itself | The app, ~30 fps over USB |
| Prism closed | Keeps running | Stops at the last frame |
| App crashes | Keeps running | Stops |
| Survives reboot | Yes, before Windows even loads | No |
| Looks like | Static, Breathing, Chase, Rainbow… | Fire, Comet, Twinkle, Scanner… |
Pick a controller effect and Prism becomes optional. It sets the mode, commits it to the chip, and the engine then goes completely idle — no frame loop, no USB traffic, nothing to fail. Kill the process and your lights carry on.
Pick a Prism effect when you want something the firmware cannot do.
Mixing is fine: run Fire on the header with your AIO and a committed Static on the other two.
The home page applies a whole look in one click. Each tile says whether it will keep running without Prism.
| Scene | What it is | Runs on |
|---|---|---|
| Spectrum | Every hue, laid along each header | Controller |
| Breathe | A slow fade, easy to sit next to | Controller |
| Steady | One clean colour that never moves | Controller |
| Aurora | Cold greens and violets, drifting | Prism |
| Ember | Banked coals, warm and restless | Prism |
| Glacier | Pale ice, breathing slowly | Prism |
| Nightfall | Deep indigo bleeding into rose | Prism |
| Pursuit | A cyan comet on a dark field | Prism |
Per header, independently:
- Effect — ten controller effects, eight Prism ones
- Colours — up to four, with an HSV picker; Gradient and Pulse blend across the whole set
- Speed, Brightness, Reverse direction
- LED count — how many LEDs are actually wired to that header, up to 120
- Enabled — off means off, committed, so it stays off
Apply this look to every header copies the current channel onto the others.
The preview strip is the real renderer running the real effect, so what you see is what the LEDs do.
Set it to the number of LEDs physically on that header — three fans with eight LEDs each is 24. Too low and the far end of the strip stays dark; too high and Prism wastes USB bandwidth on LEDs that are not there. Keeping a channel at 20 or under also halves the packets per frame, which noticeably helps the frame rate of Prism-rendered effects.
The failure modes that make Armoury Crate feel unreliable are each handled deliberately:
Nothing owns the device but the engine. One background thread holds the HID handle. Every write goes through it, and every failure path leads back to "try to reconnect", never to a dead app.
Reconnects by itself. If the controller drops off the bus, the engine retries with a backoff up to 8 seconds and re-finds the device by VID/PID — the device path changes across a USB re-enumeration, so reopening the old path would not work.
Comes back from sleep. On resume Prism re-asserts, then re-asserts again four seconds later, because the USB path often is not settled the first time.
A UI bug cannot take the lighting down. The dispatcher exception handler marks exceptions handled and logs them; the lighting thread is independent of the window.
Your settings survive. config.json is never written in place — a save goes to a temp file, is
flushed to the physical disk, then swapped in atomically with the old copy kept as config.bak.
Reading it is the harder half. Prism starts at login, where that read races the antivirus and the
rest of the boot storm, so it opens the file shared and retries with a backoff out to about nine
seconds before falling back to config.bak. If it still cannot be read, Prism:
- runs on defaults but refuses to save, so your file is never overwritten by them;
- does not push those defaults to the controller, so your actual lighting is left alone;
- says so in a banner across the top rather than a status line you might miss;
- and keeps retrying in the background, adopting your real settings the moment the file becomes readable — unless you have already changed something, in which case what you did wins.
An earlier version retried for only 600ms and then gave up permanently for the session. That was not nearly enough at login: the file read failed, defaults loaded, saving was disabled, and every change made that session went nowhere. It looked exactly like an app that does not save.
The flash is not worn out. Commit writes the controller's own memory, so it only fires when
the hardware-side configuration actually changed, not on every slider drag.
Frames that change nothing are not sent. A static software effect costs one USB write, then silence.
LightingService writes to the same controller Prism does. With both running they fight, which is
exactly what flaky lighting looks like.
powershell -ExecutionPolicy Bypass -File tools\asus-lighting.ps1 -Statuspowershell -ExecutionPolicy Bypass -File tools\asus-lighting.ps1 -Disable-Disable records the current start mode and run state of every service and scheduled task it
touches into %APPDATA%\Prism\asus-services-backup.json before changing anything, then stops
and disables LightingService and ArmouryCrateService, disables the tasks that restart them, and
closes the lighting processes. It self-elevates, so expect one UAC prompt.
Running -Disable again never overwrites that backup. A second run would otherwise record the
already-disabled state over the good one and quietly turn -Restore into a no-op — the first
recording is the one worth keeping. Pass -ReRecord if you genuinely want to re-snapshot.
AsusFanControlService is deliberately left alone — that is fan curves, not lighting.
powershell -ExecutionPolicy Bypass -File tools\asus-lighting.ps1 -Restore-Restore puts every item back to the state it was recorded in, not a guess. Close Prism first or
the two will compete again.
RGB RAM. DDR5 RGB is not on the Aura USB controller — it sits behind the SMBus, reached
through the DIMM's SPD hub, and there is no user-mode path to it. Reaching it needs a kernel
driver: OpenRGB used WinRing0 until Microsoft Defender began quarantining it as
VulnerableDriver:WinNT/Winring0 (CVE-2020-14979), and now uses PawnIO, which is signed.
So it is possible, but it costs exactly the thing Prism was built to avoid.
The good news: the RGB controller on a DDR5 module stores its mode on the module — that is why RGB RAM lights up in the BIOS before Windows exists. Set the RAM once in Armoury Crate, commit it, and then disable the lighting stack. The sticks keep doing that forever, with nothing running.
Peripherals. Razer, Corsair and friends each speak their own protocol. The backend layer is separate from the UI, so another one can be added without a rewrite.
GPUs. A card's lighting, where the board partner fitted any, is a separate controller.
%APPDATA%\Prism\config.json. Comments and trailing commas are tolerated, so it is comfortable to
hand-edit; Prism reloads it on the next apply.
{
"app": {
"startWithWindows": true,
"startMinimized": true, // only affects the login launch
"fps": 40, // Prism-rendered effects only
"reassertOnResume": true,
"turnOffOnShutdown": false,
"accent": "#7AA2F7"
},
"channels": [
{
"index": 0,
"name": "ARGB header 1",
"ledCount": 30,
"enabled": true,
"effect": "hw.static", // hw.* = controller, sw.* = Prism
"colors": ["#00C8B4"],
"speed": 0.45,
"brightness": 1,
"reverse": false
}
]
}Effect ids: hw.static hw.breathing hw.flashing hw.spectrum hw.rainbow hw.chase
hw.chasefade hw.wave hw.flicker hw.off · sw.solid sw.flow sw.gradient sw.pulse
sw.comet sw.fire sw.twinkle sw.scanner
| Command | What it does |
|---|---|
tools\probe.ps1 |
Dumps every ASUS HID interface, then asks the controller for its firmware string and config table. Read-only. |
tools\test-lights.ps1 |
Red → green → blue → white → rainbow sweep → committed teal breathing. Proves control end to end. |
tools\check.ps1 |
Compiles the sources and prints errors without launching anything. |
tools\shot.ps1 |
Captures the Prism window to a PNG. |
tools\make-icon.ps1 |
Draws prism.ico from scratch, so no binary asset has to live in the repo. |
tools\asus-lighting.ps1 |
-Status, -Disable, -Restore for the ASUS lighting stack. |
| File | Role |
|---|---|
src/Hid.cs |
SetupAPI + hid.dll: enumerate, open, overlapped read/write with real timeouts |
src/Aura.cs |
The Aura USB protocol — identify, config table, direct mode, effects, commit |
src/Effects.cs |
The effect registry and every renderer |
src/Scenes.cs |
Whole-machine looks applied in one click |
src/Engine.cs |
The lighting thread: device ownership, reconnect, frame loop, dirty checking |
src/Config.cs |
Settings model, atomic save, resilient load |
src/MainWindow.cs |
Shell: title bar, navigation, status strip |
src/PageView.cs |
The page contract and the shared render pipeline |
src/HomeView.cs |
Landing page: controller state, scenes, channel overview |
src/ChannelsView.cs |
Per-header editor |
src/SettingsView.cs |
Settings page |
src/Ui.cs |
Buttons, toggles, sliders, fields, cards, glyphs, dialog shell |
src/UiExtra.cs |
LED strip preview, swatches, palette row, HSV colour picker |
src/SlimScroll.cs |
Scroll region with the stock scrollbar hidden and a thin overlay thumb |
src/App.cs |
Entry point, single instance, tray icon, power and session events |
src/Native.cs |
DWM chrome and the Startup-folder autostart entry |
src/Json.cs |
Small JSON reader/writer |
No XAML — every control is built in code, which is why the whole thing compiles with the in-box compiler and has no satellite assemblies. Icons are stroked vector paths rather than an icon font, because Segoe Fluent Icons is not guaranteed to be installed and a missing glyph renders as a box.
Messages are 65 bytes, report ID 0xEC.
| Command | Bytes |
|---|---|
| Identify | EC 82 → EC 02 + 16-char firmware string |
| Config table | EC B0 → EC 30 + 60 bytes from offset 0x04 |
| Direct mode on | EC 35 <channel> 00 00 FF |
| Push colours | EC 40 <0x80|channel> <offset> <count> <RGB…> — 20 LEDs per packet, apply bit on the last |
| Hardware effect | EC 35 <channel> 00 <shutdown> <mode> |
| Effect colour | EC 36 <mask hi> <mask lo> <shutdown> <RGB at 5+3×slot> |
| Commit | EC 3F 55 |
From config_table: [0x02] addressable headers, [0x1B] onboard LEDs, [0x1D] RGB headers,
and a 6-byte descriptor per header from 0x03 whose fourth byte is the LED ceiling.
The window never appears. Run Prism.ps1 directly instead of the .vbs so you can see the
console, and check %APPDATA%\Prism\error.log.
Status says "No Aura controller found". Run tools\probe.ps1. If it lists the device but
cannot open it, something is holding it — run tools\asus-lighting.ps1 -Status.
Colours flicker or snap back. LightingService is still running. tools\asus-lighting.ps1 -Disable.
Prism-rendered effects look choppy. Lower the LED count per channel, or drop the frame rate in Settings. Three channels at 30 LEDs is six USB packets a frame, which caps out around 30 fps. Controller effects are unaffected — they cost nothing.
Lights are wrong after waking up. Tray icon → Re-apply now.
It came up with default channels instead of mine, and nothing I change sticks. You will see a
banner across the top saying the settings could not be read. Your file is safe — Prism will not
overwrite it, and it is still retrying; leave it a moment or press Retry now. Your settings are
in config.json with a second copy in config.bak, both under %APPDATA%\Prism. Restarting also
works. If it happens at every login, your antivirus is holding the file longer than Prism waits;
adding %APPDATA%\Prism as a scan exclusion fixes it at the source.
GPL-2.0-or-later. See LICENSE.
The Aura USB protocol was worked out from OpenRGB's
AsusAuraUSBController, which is GPL-2.0-or-later. Prism is an independent implementation in C#
with no code copied, but the protocol knowledge comes from that project, so Prism carries the same
licence rather than trying to argue its way out of it.
Protocol notes also from the OpenRGB wiki.
If you fork this, the practical consequence is that your changes stay open too.