🇲🇽 Tecnología | IA | Negocios | Espiritualidad | Ideas que conectan.

Flying Toasters across four monitors, in 64 kilobytes

I reinstalled a 1995 Flying Toasters screensaver on Windows 11. It only ran on one of my four monitors, so I wrote a wrapper. The first version weighed 75 MB. The rewrite weighs 64 KB. A story about pure Win32, .NET bloat, and the discipline of not patching what isn't yours.
Flying Toasters across four monitors, in 64 kilobytes

This deserve a post in English

I have a soft spot for the After Dark "Flying Toasters" screensaver.
The original came out in 1989, the cultural peak was probably 1993,
and a community-rebuilt freeware version from 2017 has been quietly
sitting on my hard drives ever since I downloaded it from somebody's
personal blog years ago.

When I set up my new Windows 11 box this month I reinstalled it.
Booted, locked the screen, watched a tiny squadron of poultry-themed
kitchenware appear on my primary monitor — and only on my primary
monitor.

The other three displays sat there like uninvited bystanders.

This is the story of how I fixed that, why my first attempt at fixing
it weighed seventy-five megabytes, and how the second attempt is
sixty-four kilobytes.

The whole thing is on GitHub:
chaoticgeniu5/flying-toasters-2026-multimonitor.


What was actually wrong

My first instinct was that this was a Windows 11 quirk. Some
DPI-awareness regression, maybe a SysWOW64 file resolution thing.
That's the kind of bug I expect from a binary written for an OS that
shipped eight major versions ago.

It wasn't. I cracked open the .scr (which is just a renamed PE
executable — Windows doesn't care, anyone can ship one) and looked at
the imports table. What I expected to see was something like
EnumDisplayMonitors, GetMonitorInfo, maybe MonitorFromWindow.
What I actually saw was:

GetSystemMetrics
GetMonitorInfo
CreateWindowEx
... (no EnumDisplayMonitors anywhere)

The engine never asks Windows about the monitor topology. It calls
GetSystemMetrics(SM_CXSCREEN/SM_CYSCREEN) — which gives you the
size of the primary monitor — and creates exactly one window that
size. As far as the program is concerned, the entire universe is the
primary display. There are no other monitors because nobody told it
they exist.

This isn't a Windows 11 problem. This is a 1990s assumption baked
into the source code. It would have shipped exactly the same way on
Windows 95.

So the "fix" wasn't going to be a one-line patch. It would mean
rewriting the engine's window-management code from inside the
compiled binary. Ghidra-time. That's a multi-day project even when
you have decent symbols (and I didn't), and it would still leave the
render loop assuming a single Direct3D 9 swap chain. The juice
wasn't worth the squeeze.

So I did the only thing left: I wrote a wrapper.

The wrapper

A Windows screensaver is just an .exe with a special command-line
contract. Windows invokes it with one of:

  • /s — run fullscreen
  • /p HWND — render a small preview into the given window handle
    (this is what the Settings dialog uses for its tiny preview pane)
  • /c[:HWND] — show the configuration dialog

Most relevant: the engine already knows how to render into a
parent HWND
because of the preview mode. If I create a fullscreen
window per monitor and tell the engine "render in this HWND," it
will. It just doesn't know that's what it's doing — it thinks it's
filling a settings preview pane.

The wrapper, then:

  1. Calls EnumDisplayMonitors to find every display.
  2. Creates a borderless WS_POPUP host window on each one, sized to
    that monitor's bounds, topmost, no taskbar entry.
  3. Spawns a copy of the original engine in /p HWND mode for each
    host window.
  4. Installs system-wide WH_MOUSE_LL and WH_KEYBOARD_LL hooks so
    any input — anywhere on any display — kills every spawned engine
    and tears down every host.

The whole thing is about 400 lines of C.

That's all four of my displays running independent toaster swarms.
The widescreen panel in the middle, the two stacked above it, and
the laptop on the left — each spawning its own engine process,
each rendering into a window the wrapper made.

Detour: my embarrassing first attempt

I wrote the first version in C# / .NET 8 with WinForms. Why? Because
it was the path of least resistance. I had the .NET 8 SDK already
installed, the Win32 interop is well-trodden in C#, and
System.Windows.Forms.Screen.AllScreens gives you a clean monitor
list with one line.

I wrote it, compiled it, set PublishSingleFile=true,
SelfContained=true, PublishReadyToRun=true,
EnableCompressionInSingleFile=true, all the modern .NET
single-file knobs, and got an executable.

It was 75 megabytes.

Seventy-five megabytes for a program whose entire job is to call
EnumDisplayMonitors, CreateWindowEx, and CreateProcess in a
loop. Most of that 75 MB is the .NET runtime itself, dragged along
because self-contained means self-contained. Compressed, ReadyToRun,
trimmed where possible — still 75 MB.

For a screensaver. From 1995. To run on top of an engine that's 2.5 MB.

It worked perfectly. It was also offensively wasteful. Shipping it on
a blog with a "Download (75 MB)" link would feel like exactly the
kind of thing I make fun of other people for. So I rewrote it.

The C version

Same code, different language. The Windows API is the same Windows
API. EnumDisplayMonitors, CreateWindowExW, CreateProcessW,
SetWindowsHookExW, WaitForSingleObject, TerminateProcess. There
isn't a single thing I needed C# for.

static BOOL CALLBACK EnumMonitorsProc(HMONITOR hMon, HDC hdc,
                                      LPRECT lprcMon, LPARAM lParam)
{
    MONITORINFO mi = { sizeof(mi) };
    if (!GetMonitorInfoW(hMon, &mi)) return TRUE;

    HWND h = CreateWindowExW(
        WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOACTIVATE,
        WND_CLASS_NAME, APP_TITLE,
        WS_POPUP | WS_VISIBLE,
        mi.rcMonitor.left, mi.rcMonitor.top,
        mi.rcMonitor.right  - mi.rcMonitor.left,
        mi.rcMonitor.bottom - mi.rcMonitor.top,
        NULL, NULL, GetModuleHandleW(NULL), NULL);

    if (h) g_hosts[g_hostCount++] = h;
    return TRUE;
}

That's the entire monitor-discovery-and-hosting block. Five lines of
business logic in a callback, no framework needed.

I used Zig as the toolchain because it ships
clang plus a complete mingw-w64 in a single 80 MB download with no
system install. winget install zig.zig and you're compiling
Windows binaries. The build is one command:

zig cc src/wrapper.c src/wrapper.rc -o flyingtoasters2026.scr `
  -Oz -target x86_64-windows-gnu -municode `
  -Wl,--subsystem,windows -Wl,--gc-sections -Wl,-s `
  -ffunction-sections -fdata-sections `
  -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-stack-protector `
  -lkernel32 -luser32 -lshell32 -lgdi32 -ladvapi32

-Oz is "optimize for size" (more aggressive than -Os). The flag
soup turns off C++ exception tables, stack canaries, and dead-code
sections. The result:

Build Size
.NET 8 self-contained, single-file, ReadyToRun, compressed 75,542,499 B
C / Zig -Oz 64,000 B

That's a 1180× reduction. Same behavior, same multi-monitor
support, faster startup (no native-library extraction step on first
run), and zero runtime dependencies. The full installer for the
wrapper plus the bundled engine plus the source code plus the README
is 3.4 MB.

Things I deliberately didn't do

A wrapper like this is the kind of project that begs for scope creep.
I had to keep telling myself no.

  • No engine patching. The engine binary stays untouched. If the
    original author ships an update, I drop the new file in and
    rename it. Done.
  • No DirectX hooking. The engine's render path is the engine's
    problem. The wrapper just owns windows and processes.
  • No Settings UI extension. The engine has its own configuration
    dialog. I just forward the /c invocation to it. The wrapper has
    no preferences of its own.
  • No service / scheduled task / autostart shenanigans. It's a
    screensaver. Windows handles invocation. Stay in your lane.

The whole point of the project was "make the toasters fly on every
monitor without doing anything else stupid." I think the 64 KB
figure is partly because of that discipline.

Get it / fork it / break it

The repo is here:
chaoticgeniu5/flying-toasters-2026-multimonitor

Direct downloads from the v1.0.0 release:

  • Setup.exe (3.4 MB) — Inno Setup installer, registers the screensaver in Settings.
  • Portable.zip (1.6 MB) — same files plus a PowerShell installer for those who prefer to see what's being copied where.
  • SHA256SUMS.txt — verify your downloads.

The wrapper is MIT-licensed. The engine is a community freeware
reconstruction with no commercial intent — full credits and the
"please contact me if I should yank this" note are in
CREDITS.md.

Pull requests welcome, especially:

  • An MSVC build script (right now only Zig is wired up)
  • A "Plan B" mode where the engine runs on the primary and the other
    monitors get blackout windows (lower GPU usage, sometimes preferable)
  • An ARM64 target

If you've installed it and the toasters now fly across your whole
desk, let me know. That's pretty much the entire point.

Discusión de miembros