PPortal streams your PS5 or PS4 to a window that behaves like the rest of Windows: WPF on the system Fluent theme, ten bits of colour composed by DirectComposition, and hardware decode on the card the machine already has. It reads the consoles and the pairings you have already set up, so the first run is not a setup.
receive0 bytesreordertimedreassembletimedcorrecttimeddecodeyour GPUpresent47 µsEvery stage above is timed on every session and written to a file on your machine, so a build that got slower can be compared against the one before it rather than argued about. compare-baselines prints the p50, p99 and maximum of each stage, and the difference between two runs.
The stream is the hard part and it was already solved by the Chiaki and chiaki-ng projects. What was left was everything around it: a window that follows the OS theme, a decode path chosen on the machine's own answer, and an installer that does not carry a second browser. PPortal is that half, rewritten on .NET 10.
WPF on the built-in Fluent theme, with ThemeMode="System", so light and dark follow the OS with no extra package and no theme of its own to keep current.
Signing in to PSN uses the WebView2 runtime that is already on the machine, instead of carrying a copy of Chromium inside the download for one login screen.
Registered consoles, manual and hidden hosts, controller mappings and the PSN token are read from the store you already have, resolved against the profile you are actually signed into.
The tuning goes to NVIDIA first, and first is not only: what must keep working on a machine with no NVIDIA card is written down as a contract rather than left as an intention.
The obvious way to get a decoded frame into a WPF window refuses anything wider than eight bits per channel, which rules out HDR before the stream starts. So the video does not go through WPF at all: libplacebo renders it into a shared Direct3D 11 texture, and DirectComposition puts that texture on screen as a ten-bit plane with the window's own overlay composed above it.
Of the three arrangements that compose, one keeps both the ten-bit plane and the stream HUD visible. That is the one it uses, and the other two were built and looked at before it was chosen.
The present interval comes from the refresh rate of the display the window is on, and a display that changes rate mid-stream changes the pacing with it rather than drifting a frame at a time.
libplacebo's scalers, debanding, colour mapping and tone curves are exposed on two screens, with presets for the settings nobody wants to learn and every slider underneath for the people who do.
A remote play client is used from a sofa, so a screen that can only be reached with a mouse is a screen that cannot be reached. Focus moves by the D-pad and the sticks on every screen, and the button that moves it is the one the mapping screen says it is.
PPortal opens the application when you run it with nothing. The rest are the diagnostics for when a pad, a driver or a machine disagrees with you:
--selftest run the host's own assertions and exit with the verdict
--controllers print what SDL sees, for a pad that is plugged in now
--capture-controller log presses for twenty seconds; add --analog for the sticks
--analog with --capture-controller: include the axes, which flood the log
--map-controller open the mapping screen against a real pad
An unrecognised flag is refused with this list and a non-zero exit, rather than silently opening the window.The console list is where the application opens: the consoles discovery found, the ones you added by address, and the ones you hid, merged into one list that says which is which. Everything else is reached from it.
The console answered and is awake, so the row streams. This is the only state where that button does anything, and it is the only state where it is offered.
The console answered and is asleep. The row wakes it, and the wake and the connection are two steps rather than one that silently retries.
Nothing answered at that address. The console you registered stays in the list saying so, because a console that disappears when the network hiccups is one you register again.
Latency claims are cheap. This one leaves evidence: each session appends one line to a record on your machine naming the decoder, the renderer, the requested bitrate and both loss settings, with the timing of every stage of the frame path beside them.
The record is a file in your own application data. Nothing is uploaded, and there is no account to sign in to for any of it.
Each section above has a page to link at, whether from a README, an issue or a search result.
WPF's image path refuses anything wider than eight bits per channel, so the video is composed beside the window instead: libplacebo into a shared Direct3D 11 texture, and DirectComposition with the overlay above the plane.
Read the page →Directional focus on every screen as an attached property, SDL device and binding strings read on the thread that owns them, and a mapping screen that captures the press you meant on any pad SDL can map, an Xbox controller included.
Read the page →Discovered, manual and hidden consoles in one list; registration dialogs that keep the rules of each console generation apart; and a settings screen that stores what it always stored.
Read the page →Receive, reorder dwell, reassemble, error correction and decoder send-to-pull are timed per session, alongside a latency floor and the configuration that produced them.
Read the page →Registered consoles, manual and hidden hosts, controller mappings and the PSN token are read from the existing store, resolved against the Windows profile you are actually signed into.
Read the page →A list of what a program refuses is more useful than another list of what it does, because it is the half you cannot find out by reading the features.
The target is Windows by construction, not by default. Keeping a second platform alive would mean a second application with a shared name.
A portable toolkit would give back none of the Win32, DXGI and WebView2 access the screens and the video path depend on, which is the whole reason WPF was chosen.
A machine with no NVIDIA card keeps hardware decode, a neutral renderer and an SDR present. What is missing is missing from the menu, not explained in a warning.
Screens were reproduced rather than reimagined, down to the settings whose stored values do not match their labels, because a screen that changes shape cannot be judged against the one it replaced.
The session record exists so that two builds can be compared on your machine. It is a file on your disk, it is not uploaded, and there is no account anywhere in the application.
Version 1.10.0, x64, Windows 10 or 11. It installs under an identifier of its own, so an existing chiaki-ng installation is left where it is rather than upgraded in place, and the two can be run one after the other on the same machine.
The application is a single self-contained executable with the native libraries it loads placed beside it. Signing in to PSN uses the WebView2 runtime Windows already carries, so there is no browser inside the download.