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).
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. 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.