This is a minimal Windows desktop wrapper around the original Rezmason/matrix web project.
It uses WinForms + WebView2 to load the included web/ folder and runs fully offline.
Current version: 1.0.0 — see release notes.
- Live preview in the configurator. Open a second window from the configurator's toolbar to see the rain effect update in-place as you tweak fields (debounced 250ms).
- Argument reference embedded.
MatrixDesktop.exe --help-fullopens a scrollable reference dialog with the full argument guide. The configurator's?button shows the same content. No external file required. - Sleep/lock aware. When the workstation locks or the machine sleeps, the WebView suspends (no GL / no audio / no timers). Resumes automatically on unlock / wake.
- Crash diagnostics. Both EXEs install an unhandled-exception handler that writes
a
.dmpto%LOCALAPPDATA%\MatrixDesktop\dumps\and surfaces the path in the error dialog. - Configurator polish.
- Dark/light theme toggle (persisted).
- Out-of-range numeric inputs show a red border + allowed-range hint inline.
- "Export .ps1" button copies the current command as a runnable PowerShell script.
- Automated releases. Push a
v*tag and GitHub Actions builds, zips, and publishes a Release with both EXEs + assets + SHA256.
Some Visual Studio setups (especially those configured to treat warnings as errors) will balk at:
- NETSDK1137 — using
Microsoft.NET.Sdk.WindowsDesktopis unnecessary for .NET 5+. - MSB3277 —
Microsoft.Web.WebView2can pull in a WPF control assembly reference (Microsoft.Web.WebView2.Wpf.dll) even for WinForms-only apps, which may trigger aWindowsBase4.0/5.0 conflict warning.
This solution addresses both:
- The project uses
<Project Sdk="Microsoft.NET.Sdk">with<UseWindowsForms>true</UseWindowsForms>. - The project removes the WebView2 WPF reference during build (WinForms-only), and also suppresses MSB3277 as a safety net.
This version focuses on portable distribution hygiene and leaner publish output without touching the upstream visualizer logic.
WebView2 keeps a browser profile/cache (“user data folder”). By default, that lives under the user’s profile in %LOCALAPPDATA%.
This wrapper stores WebView2 user data under AppData instead of beside the EXE:
- Primary:
%LOCALAPPDATA%\MatrixDesktop\WebView2\ - Fallback:
%APPDATA%\MatrixDesktop\WebView2\ - Last-resort fallback:
%TEMP%\MatrixDesktop\WebView2\
This avoids WebView2 startup failures when the app is launched from read-only folders, network-style paths, or WSL-mounted locations such as Z:\home\....
The bundled web\ runtime assets are also staged into AppData before WebView2 maps them:
- Primary:
%LOCALAPPDATA%\MatrixDesktop\Web\ - Fallback:
%APPDATA%\MatrixDesktop\Web\ - Last-resort fallback:
%TEMP%\MatrixDesktop\Web\
This keeps WebView2 asset loading on a normal local Windows path even when the EXE itself is launched from a WSL-mounted folder.
The project file excludes upstream content that is not needed at runtime:
web/playdate/**(non-runtime)web/svg sources/**(non-runtime)web/screenshot.png(large)web/lib/regl.js(unminified; runtime usesregl.min.js)- all
web/**/*.mdandweb/**/*.txt(docs/notes)
This reduces build/publish copy work and slightly shrinks the portable folder.
Publish profiles now set:
InvariantGlobalization=true
This reduces self-contained output size by omitting ICU/culture data.
Tradeoff: if you later add code that depends on full culture-aware operations (rare for this app), revisit this setting.
The upstream project is configured via URL query parameters (e.g., ?version=3d&effect=mirror).
This desktop wrapper supports the same configuration by accepting command-line arguments and
appending them to the internal index.html URL.
The solution includes MatrixDesktopConfigurator.exe, a companion app for building MatrixDesktop launch arguments without hand-writing the command line.
It provides grouped controls for wrapper flags and visualizer settings, color/palette/stripe editors, named presets, a live generated command, randomization controls, an Import button for pasted MatrixDesktop commands, and a Test Argument button. Test launches are kept windowed and use --no-exit-on-any-key by default so a generated argument can be checked without taking over the desktop.
The importer accepts full MatrixDesktop.exe ... commands, raw argument lines, query strings, and simple start "" /min ... batch lines. Recognized settings populate the current draft, which can then be saved as a preset. Stripe colors are only editable and emitted when the selected effect is stripe-based: stripes, customStripes, pride, trans, or transPride.
The randomizer has three scopes: Visual preset, Colors only, and Motion/Layout. It keeps launch/window controls untouched, avoids camera/image/mirror/debug effects, caps generated stripeColors at 2–8 colors, and caps generated palettes at 3–6 stops.
On first launch, the configurator seeds three starter presets into the normal preset list: rainbow-haze, paradise, and stripe effects. They are regular user presets, so they can be edited, renamed, or deleted.
Preset storage is portable-first:
- If the publish folder is writable, presets are stored beside the configurator as
MatrixDesktopConfigurator.presets.json. - If the publish folder is not writable, presets are stored under
%LOCALAPPDATA%\MatrixDesktop\Configurator\.
In framework-dependent publish output, run:
MatrixDesktopConfigurator.exeto build/test/save arguments.MatrixDesktop.exeto run a generated command directly.
By default, the WinForms wrapper launches in borderless fullscreen across all attached monitors and any physical key press closes the app while MatrixDesktop is the foreground app (software-injected key events are ignored).
Wrapper-level flags are consumed by the desktop shell itself and are not forwarded to the embedded web app.
| Flag | Aliases | Summary |
|---|---|---|
--windowed |
- | Opens in a normal resizable window instead of fullscreen. |
--borderless |
--fullscreen, --span, --span-all, --spanall |
Borderless fullscreen across all monitors. This is the default launch mode. |
--single-monitor |
--singlemonitor |
Borderless fullscreen on the primary monitor only. |
--monitor N |
- | Borderless fullscreen on monitor index N (0-based). This implies single-monitor mode. |
--working-area |
--workingarea |
Uses monitor working areas so taskbars stay visible. Applies to borderless modes. |
--topmost |
- | Keeps the window above other windows. |
--no-topmost |
--notopmost |
Turns off always-on-top behavior. |
--exit-on-esc |
--esc-exit |
Enables physical ESC-to-exit (useful if any-key exit is disabled). |
--no-esc-exit |
--noesc-exit, --no-esc |
Disables ESC-to-exit. |
--exit-on-any-key |
--exit-on-anykey, --anykey-exit |
Closes the app on any physical key press (default). |
--no-exit-on-any-key |
--no-anykey-exit |
Disables any-key exit (ESC may still exit if enabled). |
--foreground-key-exit |
--require-foreground-key-exit |
Only exit on keypress when MatrixDesktop is the foreground app (default). |
--global-key-exit |
--background-key-exit, --global-exit-on-key |
Exit on keypress even when MatrixDesktop is not focused (use with caution). |
--hide-cursor |
--hidecursor |
Hides the mouse cursor while the app is running. |
--show-cursor |
--showcursor |
Explicitly keeps the cursor visible. |
--no-devtools |
--nodevtools |
Disables WebView2 DevTools. Handy for a more locked-down distribution build. |
--devtools |
- | Explicitly leaves WebView2 DevTools enabled. |
--help |
-h, /?, help |
Shows the built-in help dialog. |
Practical examples:
- Default launch (borderless across all monitors):
MatrixDesktop.exe
- Global key exit (any physical key closes even when not focused):
MatrixDesktop.exe --global-key-exit
- Windowed debug run:
MatrixDesktop.exe --windowed --version 3d
- Borderless on the primary display only:
MatrixDesktop.exe --single-monitor --effect mirror
- Borderless on monitor index 1 while keeping the taskbar visible:
MatrixDesktop.exe --monitor 1 --working-area --effect mirror
- Kiosk-style launch:
MatrixDesktop.exe --borderless --topmost --hide-cursor --no-devtools
- Disable any-key exit (ESC only):
MatrixDesktop.exe --no-exit-on-any-key --version classic
- Show the built-in help dialog:
MatrixDesktop.exe --help
Supported input forms:
- Raw query string:
MatrixDesktop.exe "?version=3d&effect=mirror"
- Key/value pairs:
MatrixDesktop.exe version=3d effect=mirror camera=true
- GNU-style flags:
MatrixDesktop.exe --version=3d --effect=mirror --camera=true
- Space-separated:
MatrixDesktop.exe --version 3d --fallSpeed -0.1
Notes:
- Values containing spaces or special shell characters should be quoted.
- Key-exit uses a Windows low-level keyboard hook (WH_KEYBOARD_LL) and filters injected events (LLKHF_INJECTED / LLKHF_LOWER_IL_INJECTED), so typical software-injected keystrokes (SendInput/keybd_event) do not trigger exit.
- NOTE: input generated via a virtual HID keyboard driver is often indistinguishable from physical hardware in user-mode; if you need to block those too, you would need device allowlisting logic.
- By default, key-exit only triggers when MatrixDesktop is the foreground app; use
--global-key-exitto make it close on keypress even when not focused. - If both an enable and a disable flag are supplied for the same wrapper feature, the last one wins.
- Boolean flags accept common forms like
true/false,1/0,yes/no,on/off. - Bare boolean web flags such as
--camera,--volumetric,--clickRipples, or--skipIntroare treated astrue. - Monitor indices are 0-based and come from
Screen.AllScreens; their order can change if displays are rearranged in Windows. - Any unknown parameters are passed through but will be ignored by the web app.
The following arguments are recognized by the upstream web/js/config.js URL parameter parser.
version(string) — Matrix variant preset.- Examples:
classic,3d,operator,megacity,nightmare,paradise,resurrections,trinity,morpheus,bugs,palimpsest,twilight,holoplay,neomatrixology. - Aliases also exist in the codebase (e.g.,
updated,throwback,1999,2003,2021).
- Examples:
font(string) — glyph set.- Examples:
matrixcode,resurrections,gothic,coptic,huberfishA,huberfishD,gtarg_tenretniolleh,gtarg_alientext,neomatrixology,megacity.
- Examples:
effect(string) — post-process effect.- Examples:
palette(default),plain,none,stripes,customStripes,pride,trans,transPride,image,mirror.
- Examples:
renderer(string) — graphics backend.regl(default WebGL) orwebgpu(if supported on the machine).
numColumns(int) — size of the glyph grid, clamped to 1–256.width(int) — alias ofnumColumns.density(number) — volumetric density multiplier, clamped to 0.01–4.resolution(number) — render resolution scale factor, clamped to 0.05–2.animationSpeed(number) — global animation multiplier.forwardSpeed(number) — forward motion speed in volumetric mode.cycleSpeed(number) — glyph cycling speed.fallSpeed(number) — falling speed.raindropLength(number) — raindrop length / spacing control.dropLength(number) — alias ofraindropLength.slant(number) — slant angle in degrees.angle(number) — alias ofslant.
fps(number) — target FPS (0–60).bloomSize(number) — bloom size (0–1).bloomStrength(number) — bloom strength (0–1).ditherMagnitude(number) — dithering amount (0–1).
camera(bool) — enables webcam input (used by the mirror effect).clickRipples(bool) — enables click-triggered ripples in non-mirror rain effects such asstripes,palette, orimage.clickRippleShape(string) — click ripple shape:circle(default),box,triangle, orstar. Invalid values fall back tocircle.volumetric(bool) — enables volumetric/3D rendering.glyphFlip(bool) — flips glyphs horizontally.loops(bool) — loop mode (WIP).once(bool) — render a single frame then stop.skipIntro(bool) — whenfalse, starts from the intro/blank-screen sequence (web default istrue).suppressWarnings(bool) — suppresses startup notices (e.g., hardware acceleration warning).isometric(bool) — experimental mode toggle.
glyphRotation(number) — rotate glyphs (degrees).cursorIntensity(number) — cursor glow intensity (>= 0).glyphIntensity(number) — glyph intensity multiplier (>= 0).
Color values are typically provided as comma-separated triples. RGB values are usually in the 0–1 range.
-
backgroundColor(R,G,B) -
backgroundRGB(R,G,B) — alias ofbackgroundColor -
backgroundHSL(H,S,L) -
cursorColor(R,G,B) -
cursorRGB(R,G,B) — alias ofcursorColor -
cursorHSL(H,S,L) -
glintColor(R,G,B) -
glintRGB(R,G,B) — alias ofglintColor -
glintHSL(H,S,L)
Palettes and stripes are lists:
-
palette(R,G,B,at,R,G,B,at,...) — whereatis a stop position (typically 0–1). -
paletteRGB— alias ofpalette -
paletteHSL(H,S,L,at,H,S,L,at,...) -
stripeColors(R,G,B,R,G,B,...) — stripe color sequence. -
stripeRGB— alias ofstripeColors -
stripeHSL(H,S,L,H,S,L,...) -
colors— alias ofstripeColors
stripeColors is used by stripe-based effects only: stripes, customStripes, pride, trans, and transPride.
url(string) — image URL to load wheneffect=image.
testFix(string) — internal compatibility/debug switch used by the upstream project.
- Open
MatrixDesktopApp.slnin Visual Studio 2022 (or newer). - Restore NuGet packages (Visual Studio will usually prompt you automatically).
- Build and run:
- Debug:
F5 - Release:
Build > Build Solution
- Debug:
The output EXE will be in:
MatrixDesktop\bin\Debug\net10.0-windows\MatrixDesktop.exeMatrixDesktop\bin\Release\net10.0-windows\MatrixDesktop.exeMatrixDesktopConfigurator\bin\Debug\net10.0-windows\MatrixDesktopConfigurator.exeMatrixDesktopConfigurator\bin\Release\net10.0-windows\MatrixDesktopConfigurator.exe
Publishing produces a folder you can copy to another machine.
-
In Visual Studio:
- Right-click the
MatrixDesktopproject - Publish...
- Select the profile: Portable-win-x64
- Right-click the
-
Or from a terminal at the solution root:
publish-portable-win-x64.cmdOutput:
publish\win-x64\
-
Visual Studio publish profile: Portable-win-x64-framework-dependent
-
Or run:
publish-portable-win-x64-fd.cmdOutput:
publish\win-x64-fd\
This output includes both MatrixDesktop.exe and MatrixDesktopConfigurator.exe.
This is a size optimization and can break apps that rely on reflection/COM activation in surprising ways. Validate on your targets.
-
Visual Studio publish profile: Portable-win-x64-trimmed-experimental
-
Or run:
publish-portable-win-x64-trimmed-experimental.cmdOutput:
publish\win-x64-trimmed\
-
This project relies on the Microsoft Edge WebView2 Runtime being installed.
- On many modern Windows systems it is already present (Evergreen runtime).
- If not installed, the app will show an error message on startup.
-
Web assets:
- The
web/folder is copied to both build output and publish output. - Some upstream folders and docs are excluded from build/publish output to keep the portable folder smaller.
- The
-
WebView2 user data:
- The wrapper stores WebView2 profile/cache data in
%LOCALAPPDATA%\MatrixDesktop\WebView2\. - If LocalAppData is not writable, it falls back to
%APPDATA%\MatrixDesktop\WebView2\, then%TEMP%\MatrixDesktop\WebView2\.
- The wrapper stores WebView2 profile/cache data in
-
WebView2 web assets:
- The wrapper stages the bundled
web\folder into%LOCALAPPDATA%\MatrixDesktop\Web\. - If LocalAppData is not writable, it falls back to
%APPDATA%\MatrixDesktop\Web\, then%TEMP%\MatrixDesktop\Web\.
- The wrapper stages the bundled
The MatrixDesktop wrapper code in this solution is provided as-is.
The included web/ folder is the upstream matrix project and remains under its original MIT license (see web/LICENSE).