Wire-compatible with 0.24.x — everything you have already paired keeps working, and you can update one side at a time. Nothing here changes how a host and a client agree on what to send each other, so an old client on a new host (or the other way round) streams exactly as it does today; the parts that are new switch themselves on only once both ends have them.

This is the largest release so far — close to four hundred changes. The short version: the Linux and Windows apps stopped carrying FFmpeg and now decode on your graphics card directly, two long-standing faults that were quietly throwing away picture quality on NVIDIA hosts are fixed, Windows hosts bring their own sound devices instead of a third-party driver, and controllers got swept end to end.

## TL;DR

- **The Linux and Windows apps no longer contain FFmpeg** — they drive your graphics card's own video decoding. Nothing to install, nothing to keep in step with your distro, and your saved decoder preference carries over.
- **AV1 at 4K was shipping half of every frame.** An NVIDIA host sent only the top half of each picture, and had since AV1 was first wired up. Update the host.
- **HDR and 10-bit streams stop leaving half your encoder idle** — the cap that held high-refresh HDR at around 130 fps is gone. Update the host.
- **Windows hosts make their own "Punktfunk Speakers" and "Punktfunk Microphone"** instead of bundling a third-party audio cable. This needs Steam installed on the host, though it never has to run.
- **Controllers, end to end:** a wired DualSense can play a game's fine-grained haptics and its own speaker, and more than twenty separate rumble, trigger and lightbar faults are fixed across every client and both hosts.
- **Sound was never actually lined up with the picture.** Hosts have always marked audio with the moment it was captured, and no app had ever read that mark — so how far behind the picture your sound played was whatever buffering happened to settle on, and it got *worse* every time video got faster. All four apps now measure the gap and close it.
- **A few things need you to act** — a new group on Linux, a firewall port for add-on interfaces, and a change to gyro aim sensitivity. They are right below.

## Before you update

Most people need to do nothing. Check this list if any of it applies to you.

- **Linux hosts, if you use the virtual Steam Deck controller: join a new group.** That permission used to ride on `input`, which every gamepad guide tells you to join — but it can emulate arbitrary USB hardware, so it now has its own. Run `sudo usermod -aG punktfunk "$USER"` and log back in, or the virtual Deck pad stops attaching. Ordinary virtual gamepads are unaffected, and you should only join this group on a machine you trust. This is a host-side step: a Linux machine you only *stream from* needs nothing. **If that command reports `group 'punktfunk' does not exist`**, you are on one of the install paths that shipped 0.25.0 without creating it — an Arch box upgraded rather than freshly installed, NixOS, the Bazzite sysext, or a Steam Deck source install. `sudo groupadd --system punktfunk` first, then the `usermod`; a later update creates it for you.
- **Add-on interfaces moved to their own port (47993).** An existing firewall rule will not pick it up when you upgrade, and the symptom is a blank panel where the add-on's interface should be. On Linux the package prints the exact command — `sudo ufw app update punktfunk-web && sudo ufw reload`, or a firewalld reload. With Docker, publish `47993` as well. If you reach your console over a self-signed certificate, your browser needs to trust the new port once; the console shows a card with a link that does it.
- **Windows hosts now need Steam installed** for streamed audio — it never has to run. Without it the host streams video only, and picks the drivers up on its own if you install Steam later. Two new devices, "Punktfunk Speakers" and "Punktfunk Microphone", will appear in your sound settings; that is this feature working. If you already have VB-CABLE, leave it — it still works as a fallback and is not removed.
- **Saving a game with a custom launch command asks for your console password again**, and add-ons may no longer set launch commands at all. A third-party add-on that did will need updating by its author.
- **A fresh install now runs the add-on runner by default.** Upgrades are untouched — if you switched it off, it stays off.
- **If you set up a Steam Deck with the install script, rotate your console password.** It was written to a world-readable file, so any local account could read it — and fixing the permissions does not un-share a password that was already readable, which is why this one is worth actually doing rather than considering. New installs are written correctly. On an existing one, re-run `install.sh` or `update.sh` to tighten the file, then change `PUNKTFUNK_UI_PASSWORD` in `~/.config/punktfunk/web.env` and `systemctl --user restart punktfunk-web`.
- **If you play with motion controls, your aim sensitivity will change.** The gyro pipeline was wrong at every stage and is now measured against a real controller, so the numbers moved: a controller presented to games as a DualShock 4 was reporting motion **forty times too fast**, and a PlayStation pad plugged into an Android phone was reporting about **30% short**. If you turned a game's sensitivity down or up to cope, set it back. The Android case is the one people plausibly tuned around — that aim now needs a *higher* in-game sensitivity than you are used to.

## New

- **Native GPU video decoding on Linux and Windows.** The desktop apps drive Vulkan Video, Direct3D and VAAPI themselves, with a CPU fallback, and no longer ship a media library at all. Choosing a decoder works exactly as before and your saved preference is migrated. Two real changes: there is no software HEVC decoder any more — none exists that we can ship — so a machine whose graphics card cannot decode HEVC now reconnects on a codec it can finish instead of crawling on the processor; and AV1 is only requested where your card can genuinely decode it, rather than anywhere a processor decoder happened to exist. Every decoding path is now checked frame by frame against a reference decoder, so a picture that streams cleanly while being subtly wrong cannot pass unnoticed — which is exactly what was caught during development. On Windows with Intel graphics, AV1 decodes through Direct3D rather than Vulkan, which is far quicker on those cards. Update the client.
- **Your DualSense's own haptics and speaker, carried from the host.** The fine-grained feedback in the grips — as distinct from the coarse rumble motors — now crosses the stream to the controller in your hands. Needs a DualSense or DualSense Edge **plugged in by USB** (over Bluetooth the pad exposes no audio device at all), a **Windows host with Steam**, and either the Android app or the desktop session client. Anywhere else nothing changes. A game that only uses ordinary rumble keeps rumbling exactly as before.
- **The desktop console answers a mouse and a touchscreen.** Nothing in it had ever been clickable: the button bar is now live on every screen, Tab changes section, and right-click is Back. Saved host tiles gain a menu on Up — Wake, Copy link, Edit and Forget — so a renamed machine or a mistyped address can be fixed without leaving the console.
- **Hold Select to press the host's Guide button.** Hold it on its own for about a third of a second and the host sees its Guide button go down, and stay down while you hold — so a longer hold reads as a long-press, which is how a big-screen host opens its Quick Access Menu. A quick tap still goes to the game. On by default on iPhone, iPad and Apple TV, where the system keeps the controller's own Home press for itself; off elsewhere, where the raw press already reaches the host.
- **One "Game sources" list, with a Launchers row above your games.** Every source — Steam, Lutris, Heroic, Epic, GOG, Xbox, or one from an add-on — is now a row with the same controls, and add-on sources show a Running or Stopped badge. Entries can be marked as opening a launcher rather than a game, and those group above the grid instead of getting lost among your titles.
- **Get onto a host by asking instead of typing a PIN.** From the Steam Deck panel, tapping a locked host offers **Request access**: the stream waits while someone at the host approves your Deck, then comes up by itself.
- **Pin a settings profile to a host from a controller.** Every controller-driven settings screen gains a Profiles section; on Apple TV this is the only profile management there has ever been. Creating and editing profiles is still a desktop or phone job.
- **A safe-area resolution that keeps the picture out of the notch**, instead of streaming pixels hidden behind the camera housing and under rounded corners. iPhone, iPad and Android.
- **Your phone's motion sensors can act as the gyro for a controller that hasn't got one** — for clip-on pads. Off by default, iPhone/iPad and Android phones.
- **Two new command-line tools**: `punktfunk discover` lists the hosts on your network and which you have saved, and `punktfunk launch <host> --request-access` gets a headless machine admitted without a PIN. Android also gains **Copy link** on a host card, matching the other clients.
- **A lost audio packet is rebuilt exactly** rather than papered over, and hosts on a jumbo-frame network can opt into much larger video packets. Both need 0.25.0 on each end.

## Improved

- **The controller interface gets a full visual refresh.** Settings split into named sections you flick between, twelve selectable palettes that the whole interface takes its colours from, and screens that slide in place instead of covering each other. On the Steam Deck, TV and handheld interfaces.
- **Desktop audio is encoded at roughly double the bitrate** — 256 kbps stereo rather than 128 — which costs about one percent of what the video already uses and is most audible on music. Update the host; every existing client already plays whatever arrives.
- **The black screen on VPN-shaped networks heals itself in seconds, mid-stream**, with 0.25.0 on both ends, instead of needing a reconnect.
- **The Steam Deck plugin is now a launcher into the app** rather than a second client — see the section below, because some things moved.
- **The Android stats overlay stops charging your screen's own delay to the stream.** Your stream is exactly as fast as it was; the headline number gets smaller because it finally measures only the part Punktfunk controls.
- **The stats overlay finally shows the audio side** — how much sound is queued ahead of your speakers, and how far that puts it from the picture. Both numbers, because a deep buffer on a rough connection is correct and only the gap tells you the difference. On every client; previously neither existed anywhere you could see.
- **Every session now says why it ended**, so quitting a game you launched is no longer reported as trouble, and quitting takes you back to the library you launched it from.

## Fixed

- **AV1 at 4K shipped half of every frame.** On an NVIDIA host every 4K AV1 frame left with only its top half — present since AV1 was first wired up. NVIDIA's own decoder quietly tolerated it, so it looked fine on some setups and was a black screen on others. The encoder still uses every engine on the card. Update the host; no client can work around it.
- **HDR and 10-bit streams left half the encoder idle.** A rule written from one old measurement disabled split-frame encoding for every 10-bit stream, capping high-refresh HDR at around 130 fps and making fast motion look wrong. Re-measured across two generations of NVIDIA cards, splitting won every test. Update the host.
- **Your settings could silently reset to defaults.** A settings file saved from PowerShell carries an invisible marker that made the app quietly start over — and any other damaged settings, saved-hosts or profiles file did the same without a word. Both now load correctly, or say exactly what is wrong.
- **The microphone works again on iPhone, iPad and Mac.** The clients had stopped sending any microphone audio at all — recorded but never reaching the stream, silently and with no error. Separately, game audio on Apple devices clicked and crackled on Wi-Fi that delivers packets in bursts, where every other client sounded clean on the same host. Update the client.
- **Sound is now placed against the picture instead of wherever buffering left it.** Hosts have always marked audio with the moment it was captured, and no app had ever read that mark — so lip-sync was an accident of how deep the audio buffer happened to sit, and it got *worse* as the picture got faster, because a quicker decoder shortens the video side and leaves sound exactly where it was. That is why shaving milliseconds off the audio path had never helped. Every app now measures the gap and steers the buffer to close it — but never so far that sound starts breaking up: on a jittery connection it keeps the buffer and leaves a small gap rather than take the dropout. Hosts also release audio at an even pace instead of in bursts, which mattered most on virtual machines. And on Linux the app was handing the sound system up to 170 ms of audio every cycle — filling the buffer's ceiling rather than what the system had actually asked for — which by itself put sound about a quarter-second behind and then blocked the new sync from draining it, because doing so would have risked breaking up. That is fixed, and it is what the first on-glass run of this work turned up. Update both sides.
- **The host stopped pushing your whole desktop mix through Steam's voice channel** on PCs with Steam installed, where a mono or low-rate setting squeezed the entire game mix before encoding. Update the host.
- **A host on a network that carries smaller packets than usual no longer streams a permanent black screen** — connected, every gauge green, and nothing on screen, with nothing in either log. The host now measures what the path really carries and warns with the actual diagnosis. Update the host; this works with every client already out there.
- **Host discovery no longer goes permanently deaf.** A host missed on first launch is found without restarting the app — the usual trigger being the phone's local-network permission prompt, which the browse never recovered from — and every client now has a Refresh or Rescan control that really re-scans. Update the client.
- **A game you quit could pin the session as "running" until the host restarted**, with ending it by hand the only escape. Reconnecting could also launch your game a second time, and cost a reconnected session its game-exit detection for good. Update the host.
- **Controllers, top to bottom.** Rumble that faded on a Steam Deck, died for good after one hiccup on a phone, or kept buzzing after you quit; two DualSenses driving each other on Apple devices; adaptive triggers and lightbars left stuck in a game's last state on your desk after the stream ended; player-number lights that never lit on anything but a DualSense; a centred stick that read slightly off; two virtual pads corrupting each other's rumble on a Windows host. More than twenty faults across every client and both hosts.
- **HDR works on a Steam Deck out of the box.** It gave a washed-out, tone-mapped picture before, and the manual step that was supposed to fix it was documented only inside the packaging — and did not work anyway. HDR still has to be switched on in Steam's own display settings.
- **The Windows client saves your settings when it isn't installed on the C: drive.** It streamed fine and forgot everything on restart — settings, profiles and saved hosts — while showing the toggle you had just moved as if it stuck. You will need to set your preferences once more; nothing can be recovered, because it never reached the disk.
- **Camera look survives pressing Escape on an iPad**, instead of handing the pointer back to the system and leaving aim dead for the rest of the session.
- **Streaming a desktop no longer shows a second, frozen cursor** sitting under the one you are moving. Linux and macOS.
- **Automatic bitrate stops overshooting what your device can actually decode**, and a momentary stall no longer pins your stream at a low bitrate for minutes afterwards. Recovering from a brief freeze also stops making things worse. Update both sides.
- **A leftover folder from an uninstalled Sunshine or Apollo is no longer treated as a conflict**, and a host that crashed mid-session gives your screen back the next time it starts.
- **A routine system update can no longer brick the host on Arch and CachyOS.** FFmpeg 9 renamed every one of its libraries, and because the package asked only for "FFmpeg" rather than the versions it was built against, an ordinary update walked the host onto libraries it could not load — so it failed to start at all, before it could log anything, and retried forever. The package now states what it actually needs, so the update is refused rather than silently breaking your host, and it is built against FFmpeg 9. Update the host.
- **Add-ons that read a launcher's database returned zero games on every Linux host**, indistinguishable from an empty library. On Windows, an add-on listing a launcher's registry entries got nothing back for the same silent reason. Update the add-on.
- **Motion controls were wrong end to end, and are now measured against a real controller.** A pad presented to games as a DualShock 4 reported gyro forty times too fast; Apple clients sent acceleration upside-down and rotation on the wrong axes, so a turn arrived as a roll; a PlayStation pad on Android reported about 30% short. A controller that stopped sending motion — app backgrounded, pad swapped — also left the virtual pad spinning forever, and one that had never sent any claimed to be in free fall, which some games read as a wild tilt. See the note above about sensitivity. Update both sides; they are independent.
- **A Bluetooth controller's gyro now reaches the host from an Android phone.** Previously only a PlayStation pad plugged in by USB had working motion there — a DualSense, DualShock 4, Switch Pro or 8BitDo on Bluetooth had buttons and sticks but a silently dead sensor. Android 12 and newer. And when your controller has a gyro but the session's virtual pad has nowhere to put it, the client now says so on screen and names the setting that fixes it.
- **The game library works on any host address on iPhone, iPad, Mac and Apple TV.** It only ever loaded on a home network: over Tailscale, WireGuard, or to a host with a public address, the library came up empty or errored — while streaming to that very same address worked perfectly, which is what made it so confusing. Cover art is also cached on disk now instead of being re-fetched every time. Update the client; the host is unchanged.
- **The web console asks its own questions.** Sixteen grey browser confirmation boxes on destructive actions are replaced by the console's own dialogs, and the drop-downs, checkboxes and text areas in the hook editor and library forms are no longer unstyled controls at near-zero contrast. A virtual display can also be placed left of or above the origin again — typing a minus sign used to erase it before you could type the digits.
- **The Steam Deck panel shows host names instead of addresses**, and "recreate shortcuts" actually recreates them.

## If you stream from a Steam Deck

The Decky plugin is now a **launcher**: a short list of your hosts plus one button into the Punktfunk app, which has the full controller-driven interface. Some things genuinely moved, and one was removed:

- **Settings, adding hosts, and browsing a host's games** all moved behind **Open Punktfunk**.
- **Pinned Games has been removed, with nothing to migrate to yet.** Pinning now works on a host and a settings profile rather than on a game. Your old pin file is deliberately left alone on disk so a later release can migrate it.
- **The Deck's Steam and `…` buttons now stay with the Deck.** One press used to open both menus at once, with the Deck's own covering the stream. Reach the host's menus with hold-Select or the panel's new **Host menus** buttons; to restore the old behaviour, set **Steam / guide button** to **Send to host**.
- **The plugin needs the client on the Deck to be 0.22.0 or newer**, and detects an older one explicitly rather than showing an empty list.

## For developers

Protocol, ABI, driver and embedder detail — the version table, all four breaking changes, capability-bit pressure, the new decode crates and every environment variable — is in [CHANGELOG.md](https://git.unom.io/unom/punktfunk/src/tag/v0.25.0/CHANGELOG.md).

Two things there that you should not learn by surprise: **149 previously unprefixed macros in the C header are now `PUNKTFUNK_`-prefixed** (mechanical to fix, and it cannot break silently), and the **C ABI moved 14 → 17** while the wire protocol stayed at 2.
