Install, click Share this screen, send the invitation. That's the whole setup.
Get the installers from the latest release.
| System | File | How |
|---|---|---|
| Windows 10/11 x64 | PenguinStream-<version>-Setup.exe |
Installer (per-user, no admin needed) |
PenguinStream-<version>-Portable.exe |
Runs without installing | |
| Fedora 44 | penguin-stream-<version>-1.fc44.x86_64.rpm |
sudo dnf install ./penguin-stream-*.x86_64.rpm |
| Ubuntu 24.04 LTS | penguin-stream_<version>.ubuntu24.04_amd64.deb |
sudo apt install ./penguin-stream_*.ubuntu24.04_amd64.deb |
| Ubuntu 26.04 LTS | penguin-stream_<version>.ubuntu26.04_amd64.deb |
sudo apt install ./penguin-stream_*.ubuntu26.04_amd64.deb |
| Debian 13 | penguin-stream_<version>.debian13_amd64.deb |
sudo apt install ./penguin-stream_*.debian13_amd64.deb |
SHA256SUMS.txt in each release lists the checksums. Both computers need the same version. Peers on different
versions refuse to connect instead of half-connecting.
Note
Windows SmartScreen: the builds aren't code-signed yet, so Windows may say "unrecognized app". Choose More info β Run anyway.
Fedora codecs: the RPM works with Fedora's stock FFmpeg libraries, NVENC included. AMD/Intel GPU encoding (VA-API H.264) needs RPM Fusion's Mesa drivers. See docs/FEDORA.md.
Ubuntu/Debian: pick the .deb for your release. Each is built against that release's own FFmpeg, PipeWire and SDL,
and apt pulls in everything it needs. Use apt install ./file.deb (with the ./), not dpkg -i, so dependencies are installed.
- Host (the computer being shared): open Penguin Stream β Share this screen. On Wayland, pick the screen in the system dialog (and allow remote control if you want the other person to use your keyboard/mouse).
- Send the invitation privately (chat/DM). It stays the same every time you share, so your friend can keep it and reuse it. New code replaces it only when you ask, e.g. if it reached the wrong person.
- Viewer: open Penguin Stream β Connect to a screen β paste β Connect. Next time, Reconnect to the last host is one click.
- Both sides see four verification words. The host clicks Allow only if they match.
If the connection drops, nobody has to start over: the host keeps sharing on the same invitation, and the viewer reconnects by itself and gets straight back in without a new approval. Only Stop sharing or Disconnect ends it.
The stream opens in its own window. During a session either side can switch control on or off instantly:
| Keyboard & mouse | Controllers | Game mode (mouse lock) | |
|---|---|---|---|
| Host (Live session page) | allow / revoke | allow / revoke | β |
| Viewer (Live session page) | send / stop | send / stop | on / off |
| Viewer (stream window) | Ctrl+Alt+Shift+M | β¦+G | β¦+Z |
Ctrl+Alt+Shift+X toggles fullscreen, β¦+Q disconnects. Controllers appear on a Linux host through uinput and on a Windows host as virtual Xbox 360 pads (needs the free ViGEmBus driver).
Remote access works like AnyDesk's unattended access, but only for your own computer: switch it on once and use the PC from another device with nobody there to click Allow. It is off until you turn it on.
- On the PC you want to reach: Home β Remote access to this PC β switch on (you confirm first) β Copy access
code. Access codes start with
PA-.- Wayland (KDE, GNOME): if the card shows One-time setup, click Allow screen access, pick the screen and keep Allow restoring / Remember ticked. Later sessions then start without a prompt.
- Optional: Start Penguin Stream when I log in (it starts in the tray, no window). Keep this PC awake is on by default.
- On the other device: Connect to a screen β paste the access code β Connect. Tick Remember this PC on this device if you want one-click access next time.
Both devices need Penguin Stream 1.5.0 or newer for this: a 1.4.x device only understands invitations, not access codes.
What keeps it safe:
- The access code is 160 random bits, separate from your invitations, and never chosen by you. Without it a device can't find the PC, let alone complete the encrypted handshake, so it never gets as far as asking. Keep it private, like a password. New code retires the old one at once and ends any session that was using it.
- The PC always shows that someone is connected: a banner with End session, a desktop notification, the device's fingerprint and an entry in Activity.
- While remote access is on, closing the window leaves Penguin Stream in the tray. Turn off remote access, in the app or the tray menu, stops listening immediately.
- While the PC is in another live session (sharing with a friend, or viewing someone), remote access pauses and resumes when that session ends. If a friend is only waiting on your invitation, the remote session takes the screen first and the invitation works again afterwards.
- It can't reach a PC that is asleep, switched off, or not logged in: Penguin Stream runs inside your desktop session. Windows' lock screen and UAC prompts run on a protected desktop it can't capture or control.
The host's desktop audio streams to the viewer on both Windows and Linux. If you're in the same Discord call while sharing, you don't want your friend hearing everyone twice (themselves included). So by default Penguin Stream leaves voice-chat apps out of the streamed audio: Discord, TeamSpeak, Zoom, Teams, Mumble and others. You still hear the call normally. You can also leave out other apps, or stream only one app (just the game). See docs/AUDIO.md.
-
Pairing travels end-to-end encrypted over public Nostr relays, so there's no server to run and no port to open.
-
Media goes directly peer-to-peer over UDP (ICE hole punching). That works behind most CGNATs, which use endpoint-independent mapping. The app's Network page measures your NAT type and tells you what to expect.
-
If both sides are behind strict (symmetric) NATs, traffic falls back to a TURN relay. Only one side needs a relay configured. ICE pairs the other side's normal address with your relayed one, so you set it up once and the people you invite install and configure nothing. Options in Settings β Relay:
- Cloudflare (free, 1,000 GB/month): anycast, so it relays through the city nearest you. Paste a TURN key ID + API token; only 24-hour credentials are ever used on the wire.
- Credentials URL: any HTTPS endpoint returning ICE servers (e.g. Metered).
- Your own TURN server: coturn on a cheap VPS; see docs/RELAY.md.
Save & test relay performs a real TURN allocation, so "configured" means "proven working".
Every stage is chosen for latency first:
- Capture: DXGI Desktop Duplication (Windows) or the PipeWire screencast portal (Wayland), plus X11. One monitor by default, and each new desktop frame goes to the encoder the moment it arrives.
- Encode: hardware H.264 (NVENC
p1+ull, AMF ultra-low-latency, Quick Synclow_delay_brc, VA-API depth 1), no B-frames, zero lookahead, single-frame VBV so no frame takes longer than one frame interval to send. On NVIDIA the GPU does the BGRAβYUV conversion (about 3 ms/frame less CPU work at 1440p). - Transport: an unordered, zero-retransmit DTLS/SCTP data channel. A late frame is dropped rather than delayed, and the host skips non-keyframes when the send queue backs up. Loss triggers an immediate keyframe request (< 100 ms recovery).
- Encryption: AES-256-GCM, hardware-accelerated by AES-NI on every x86-64 CPU.
- Display: decoded with slice threads (no frame-threading delay) in a native SDL window, not a browser. By default each frame is presented the moment it's decoded (no V-Sync wait).
- Input runs on its own path, and relative mouse motion is coalesced per frame.
- Adaptive bitrate: the host lowers the bitrate as soon as video starts queueing and restores it when the connection is clean, so a busy upload doesn't turn into seconds of delay.
- See it: the live page measures every stage in milliseconds (capture β encode β network β decode β display) and tells you which setting would help. Profiles switch between setups in one click.
End-to-end encryption uses a Noise XX handshake (Noise_XX_25519_AESGCM_SHA256) over DTLS, with 160-bit invitations, four-word
verification, and explicit host approval for every new device. Remote access is the one exception, and only once you
switch it on: there, holding your PC's private 160-bit access code replaces the prompt. Relay operators and Nostr relays see only ciphertext. The local control
UI is loopback-only and token-protected. Read SECURITY.md for the threat model and what has not been independently audited.
Needs Node.js β₯ 20, CMake β₯ 3.20, a C++17 compiler, FFmpeg/SDL2/PipeWire/GLib development packages.
npm ci
cmake -S media -B media/build && cmake --build media/build -j
npm run app # run the desktop app from source
npm test # full suite, incl. crypto inside the Electron runtime and a real PipeWire audio session
npm run dist:linux # -> dist/penguin-stream-<version>-1.fc44.x86_64.rpm (needs rpmbuild)
npm run dist:deb # -> dist/penguin-stream_<version>.<distro>_amd64.deb (needs podman; run dist:linux first)
npm run dist:win # -> dist/PenguinStream-<version>-Setup.exe + Portable.exe (needs podman)The Windows build cross-compiles the media engine with MinGW in a Fedora container and packages it in
electron-builder's Wine container. The Ubuntu/Debian packages build the engine inside each release's own container. Nothing gets
installed on the build machine. The CLI (node node/src/cli.mjs host|connect|doctor)
is still available for headless use and scripting.
See LIMITATIONS.md for exactly what has and hasn't been verified, and CHANGELOG.md for what changed in each release.
MIT licensed. Third-party components: THIRD-PARTY.md.