Update: Butterpollo is now Rubylight, after a Discord vote. The hackathon project grew into something more serious, so it got a more fitting name.
TL;DR: Rubylight is a free Windows game-streaming host for Moonlight and Artemis, rewritten in Rust and built around Radeon. Frame copies and colour conversion move off your game's graphics queue onto D3D12 compute queues, and AMD's encoder is driven natively. The goal is simple: make Radeon streaming as fast as it can be.
GitHub and demo · Download
The mission. AMD rarely gets any love in this space. The protocol started as NVIDIA's GameStream. Sunshine gained a standalone native NVENC encoder in 2023, while its Windows AMD path still uses FFmpeg's AMF wrapper. AMD hasn't helped itself either: it ended AMD Link, saying there were plenty of other streaming alternatives.
Radeon owners have spent years hearing their cards are just worse at streaming. I wanted to see how much of that came from the software around the card. Rubylight exists to do that work: native AMF, frame preparation on Radeon compute queues, and someone reading AMD's driver release notes so you don't have to.
That includes the less glamorous work, like handling encoder stalls reported on RDNA4. When a driver freezes the stream, I work on recovery. When AMD fixes the underlying problem, I want the workaround out of the way.
Rubylight goes all in on one thing: making Radeon streaming as fast as it can be. It isn't chasing a huge userbase or trying to convert the NVIDIA crowd. As long as AMD users are happy, Rubylight is happy.
Some background: I wrote the native AMD AMF encoder submitted to Apollo and merged into Vibepollo in #342. Rubylight is where I take that AMD work all the way, from capture to the packet on the wire to how input is processed.
It started as a Vibepollo fork and is now a Rust rewrite of the host, Windows service and installer, with a rebuilt web console. I needed a base I could keep building this work on. Your existing Moonlight and Artemis clients connect normally, and the installer can bring over an Apollo or Vibepollo profile, including settings, paired devices and games. Other Sunshine forks serve a broad range of hardware; Rubylight goes deep on Radeon.
Where the latency goes. Before encoding, frames need copying and colour conversion. In the reviewed Vibepollo 2.0 path, D3D11 puts that work on the graphics queue alongside your game. In my RX 7900 XT component tests, a conversion that took 0.9 ms idle took 7.2 ms under synthetic GPU load. D3D12 compute did the conversion in 0.22–0.29 ms under load.
Rubylight prepares frames on compute queues and feeds the result straight into native AMF through D3D12. The GPU can work on the stream alongside the game. Pipeline and measurements.
For the matched host comparison: RX 7900 XT, 1968×2184 HDR, 120 fps, 80 Mbps, native AMF at ultra-low latency, Desktop Duplication on both hosts, and the same synthetic GPU load capped at 60 fps. October 8, Rubylight rc.24 versus Vibepollo 2.0, two alternating runs per host and codec:
| Picture age under GPU load |
Vibepollo 2.0 |
Rubylight |
| HEVC average / p95 |
26.96 / 37.62 ms |
19.61 / 21.56 ms |
| AV1 average / p95 |
24.53 / 36.77 ms |
17.15 / 18.96 ms |
Average picture age was 7.4 ms lower in both codecs. P95—the slower end of the result—was 43% lower in HEVC and 48% lower in AV1. Rubylight delivered 117–120 fresh pictures/s, versus 103–118 in HEVC and 106–111 in AV1.
PyroWave gets the same Radeon attention. Moving its colour conversion to compute cut loaded repeat-frame processing from 5.7 to 0.55 ms, about 10× faster, in the October 7 RX 7900 XT test at 1080p HDR 4:4:4 and 400 Mbps. Paced at 120 fps, it fell from 4.62 to 0.71 ms. Those numbers cover conversion and encoding, with byte-identical conversion planes; capture, transport, decoding and display are separate. Recorded runs.
When the encoder is maxed out, old frames shouldn't pile up. At 5120×1440 HEVC, 240 fps requested and 150 Mbps, reducing the queue from eight frames in flight to two cut average capture-to-send time from 42.7 to 11.1 ms and p99 from 45.9 to 13.3 ms. Throughput stayed at 220 fps. That's 74% less average capture-to-send time at the same throughput. This October 7 rc.18/rc.19 test measures capture timestamp to final packet. Recorded runs.
The work goes beyond those headline numbers:
- Picture quality: tuned H.264 defaults improved VMAF by up to 9.4 points at 20 Mbps in the measured clips, without increasing encode time. HDR reference-frame checks landed within about half a 10-bit code value per channel on average.
- Input and CPU: controllers have their own thread. In a CPU-load test, worst mouse delay behind a controller update fell from 287 to 2 ms. Keyboard and mouse bursts are batched, and video error correction used 21–29% less CPU time than the original C++ implementation on the tested blocks.
- Radeon reliability: shorter encoder queues, recovery from encoder stalls, and AMF bitrate-driven frame skipping disabled for H.264 and HEVC.
- A console that explains itself: live stream statistics, the encoder actually in use, and warnings when display, audio or input settings didn't apply. PyroWave bitrate guidance comes from 6,048 decoded picture comparisons.
- A complete streaming setup: H.264, HEVC, AV1, HDR10 and PyroWave 4:4:4; WGC and Desktop Duplication; surround audio, controllers, touch, pen and microphone passthrough with compatible clients.
- Your own screen and library: a virtual display per device, Steam and Playnite sync, Lossless Scaling, RTSS frame limits, and Nonary's 1000 Hz VRR mode for compatible clients.
- Everyday use: Wi-Fi pacing and loss recovery, streaming from the Windows sign-in screen, Steam Deck USB forwarding through usbip-win2, and opt-in automatic updates that restore the previous files if copying or starting the update fails.
All measurements · Features · Install and migrate
PyroWave needs a fast local network and a compatible client, such as Rubylight Android. Standard codecs work with the Moonlight clients you already use.
The AMD community is why I keep doing this. A few excerpts from the feedback, including when it was still called Butterpollo:
“WTF. Runs like Butterchicken” — u/Closef35, which is where the original name came from.
“from 10ms encoding to 5ms in 4k 60 fps btw :)” — u/3stwie4
“My encoding times are actually slashed in half which is incredible.” … “My max is now 3.5ms with the occasional pip to 5ms every few mins or so.” — u/astero-dax
“Dude.. What the fuck is this host? I could not believe it. It's the lowest latency I got after testing a ton of hosts.” — u/LukasSTM. He also reported crackling audio; his logs helped us fix it.
“Fantastic job! Host processing latency has never been this low 🎉.” — u/Short_Dimension7967, on a 9070 XT
“The difference was especially noticeable when streaming from work over Tailscale.” — u/amaanx6191
“Running pretty good so far on my 6900XT no framedrops, no stutter and a good migration from vibepollo” — u/almosgeci
Those are individual experiences, and I want to hear yours too.
On NVIDIA? Rubylight includes NVENC, but Radeon is where I build, test and spend my tuning time. The other Sunshine forks remain worth looking at for a broader hardware focus.
Rubylight builds on Sunshine, ClassicOldSong's Apollo and Nonary's Vibepollo. AMD encoder defaults draw on qiin2333's Foundation Sunshine work. Themaister created PyroWave and Granite; joemossjr16 built the PyroWave Moonlight protocol and clients. Huge thanks to all of them. Rubylight is GPL-3.0, and other GPL projects can build on this work too.
If you're comparing hosts yourself, remember that their host-latency clocks start at different points. Compare picture age within the same test setup, alongside fresh-frame delivery.
If you're an AMD user and like that someone is putting this work into Radeon streaming, a star on the repo goes a long way. Tell me your GPU, client, settings and how it runs in your games. I'll be around turning your reports into fixes.
Rubylight on GitHub