A native macOS client for GeForce NOW - built for Mac, built for controllers.
⬇ Download the latest release · What's new · Build from source
Independent community project. Not affiliated with, endorsed by, or sponsored by NVIDIA. NVIDIA and GeForce NOW are trademarks of NVIDIA Corporation; other product and storefront names are trademarks of their respective owners. Bundled components, their licenses, and trademark details are listed in the Third-Party Notices, which also ships inside the app bundle.
Huge thanks to @Jayian1890 - the main contributor of all base and core functionality of openNOW-Mac. This project stands on that foundation.
OpenNOW is written in SwiftUI from the ground up and streams GeForce NOW sessions over NVIDIA's native NVST protocol. The pitch is simple: treat a Mac like a Mac, and treat a controller like a controller. Browse your library and launch in seconds, stream at up to 5K, record the runs you want to keep, and play with a Steam Controller 2026 without ever installing Steam.
GeForce NOW works on a Mac, but the official client leaves a lot on the table - a mouse-and-keyboard web view, pillarbox bars baked into every ultrawide stream, and no love for the controllers people actually game with. OpenNOW fills those gaps with a real Mac app, and then keeps going.
| 🎮 Steam Controller 2026 support | Wired, Bluetooth LE, and both 2.4 GHz dongles. Full HID parsing, haptics, back grips, trackpads, custom mappings - no Steam required. |
| 🎯 Gamepad API for Xbox and PlayStation pads | Read Xbox, DualSense and DualShock 4 controllers from their own reports instead of Apple's framework, so the thumbsticks reach the game without the dead zone Apple adds - and switch between the two mid-game from ⌘G. More ↓ |
| 🖥️ Built for ultrawide | 21:9 up to 5120×2160 and 32:9 up to 5120×1440, HEVC and AV1, and six ways to kill the black bars. More ↓ |
| 🔼 Upscaling | Off, Spatial, or MetalFX, targeting 2K/4K/5K, with live Clarity and Noise Reduction sliders and gamepad navigation. More ↓ |
| ⚡ Native NVST transport | Stream over NVIDIA's NVST protocol on OpenNOW's own native stack - RTSPS control, raw-SRTP video, VideoToolbox decode - with no vendor runtime in the bundle. More ↓ |
| ⏺️ Record your sessions | One keystroke (⌘R) captures gameplay locally, with a browsable library and opt-in trim/crop/export editor. More ↓ |
| 📚 Your whole catalog | Hero rotation, game rails, search and filters, store ownership picker, persistent Library and Favorites - plus a live banner that drops you straight back into an active session. |
| ☁️ Menu bar control | Track and drive the session from the menu bar - live status, elapsed clock, Resume / Pause / End, and Continue Playing - and keep the app running with no window at all. More ↓ |
| ⚓ Dock integration | Right-click the Dock icon for your three most recent games, New Session, and Open Recordings - with a badge while a seat is waiting and a progress bar for a queue or an export. More ↓ |
| 🌐 Remote Co-Op | Invite a friend from a browser link and hand them a player slot in your session - hosted by OpenNOW itself, no server to deploy, host-approved, native input path. |
| ⌨️ On-screen keyboard in-stream | Steam + X summons a Steam Deck-style keyboard right over the game - dual trackpads aim, L2/R2 or a pad click types. Tap the ⬍ key to flip it to the top of the screen when it overlaps something important. Works on any controller. |
| 🔊 5.1 surround | Real multi-channel game audio: OpenNOW asks the server for the surround layouts it can send, decodes 5.1 natively and puts each channel on the right speaker, with a stereo mix for recordings and Remote Co-Op. More ↓ |
| 🖼️ VRR frame pacing | On a variable refresh rate display, each frame is shown the moment it arrives with vsync on, so the screen refreshes with the game instead of on a fixed clock. More ↓ |
| 🖱️ Smooth mouse aim | Raw Mouse Input sends every movement the mouse reports - up to 1,000 a second - instead of the ~120 batches macOS delivers, so the camera turns smoothly in fast games. More ↓ |
| 🔔 Session ready | Queue in the background: pick a system notification or have OpenNOW come to the front the moment your seat is ready. |
| 🔎 Settings you can find | Nine destinations named for what they hold, and a search that answers the word you know: type 5.1, black bars or vsync and it lands on the setting. More ↓ |
| 💬 Discord Rich Presence | Your friends see what you're playing, automatically. |
| 🕹️ Full controller navigation | Drive the entire app - catalog, details, settings - from the pad. Never reach for the mouse. |
| 📊 Real diagnostics | Live stream HUD, session timers, network stats, and exportable logs when something goes wrong. |
- Download
OpenNOW.dmgfrom the Releases page. - Open it and drag OpenNOW to Applications.
- Launch from Spotlight. If macOS blocks it, right-click the app → Open.
Requires macOS 15.6 or later and your own GeForce NOW account.
Pick your shape, then your resolution - 16:9, 16:10, 21:9, or 32:9. The wide end tops out at 5120×2160 on 21:9 and 5120×1440 on 32:9, all at 30/60/120/240 fps. HEVC carries true 5K streams where H.264 hardware decode runs out of headroom, and AV1 leans in whenever bandwidth is the bottleneck, decoded in hardware on M3-and-later Apple silicon. 10-bit 4:2:0/4:4:4 colour is there when the codec supports it, HDR10 rides a 10-bit HEVC stream straight to an EDR drawable on both transports, and any codec your Mac can't decode greys itself out instead of failing mid-session.
On a 4K TV, pick a scaled (HiDPI) mode in System Settings → Displays. OpenNOW tells the cloud PC your display's DPI from macOS's scaling, so a TV run at 1× gets a Windows desktop at 100% scale, with text too small to read; a scaled mode keeps the full 4K stream and enlarges the remote desktop to match.
GeForce NOW bakes pillarbox columns into 16:9-only titles - real black pixels, not window padding, so a wide monitor is stuck with them. OpenNOW detects those bars in the incoming frames and lets you decide what fills them:
| Mode | What you get |
|---|---|
| Black | Leave the encoded bars alone. Zero cost, default. |
| Colour | Flat fill in any colour you pick. |
| Blur Mirror | Mirrors the picture edge outward, blurred. Seamless, no distortion. |
| Blur Zoom | Blown-up blurred copy of the frame behind the sharp image. |
| Stretch | Fills the full width, pushing distortion to the edges so the centre stays true. |
| Crop | Scales to fill and trims top and bottom. No bars, no warping - costs vertical view. |
Blur modes take an adjustable dim. Everything but Black runs through the custom Metal render path.
Three tiers, three targets - pick a resolution the game doesn't actually render at and let the client fill in the rest.
| Tier | What it does |
|---|---|
| Off | Present the decoded frame as-is. Zero cost, default. |
| Spatial | A custom Metal shader: edge-aware sharpen and denoise, tuned per source resolution. |
| MetalFX | Apple's spatial scaler, perceptual color processing, best detail reconstruction at a real GPU cost. |
Pick a target of 2K, 4K, or 5K and the output caps there - never past your actual window or display size, so it never spends GPU time supersampling beyond what you'd see anyway. Clarity and Noise Reduction sliders tune the Spatial/MetalFX pass live, mid-stream. Every control - tier, target, both sliders - is gamepad-navigable from the in-stream HUD (⌘G), no mouse required.
Every GeForce NOW launch and resume uses NVST - NVIDIA's native streaming protocol - on OpenNOW's own stack, with no NVIDIA libraries in the bundle. Existing global and per-game transport selections are ignored; all other profile settings are preserved.
- OpenNOW's own implementation, no vendor runtime - an RTSPS-over-WSS control channel (OPTIONS → DESCRIBE → SETUP → ANNOUNCE → PLAY), a client-generated SRTP master key, and a video handoff derived from the seat's answers the same way the native client derives it.
- Native video and input - Mjolnir video access units (H.264, HEVC, and AV1) decode through VideoToolbox. Keyboard, mouse, text, gamepad input, and control messages use the seat's SCTP data channels; game audio and microphone use SRTP audio streams on the same ICE/DTLS bundle.
- Live native telemetry - latency, jitter, bitrate, packet and frame loss in the in-stream stats HUD, plus a network governor that adapts bitrate to path conditions.
- Microphone on NVST - when the seat offers bundle mic in DESCRIBE (every current seat does), the bundle carries a send-only Opus mic section exactly like the official client, driven by push-to-talk / voice-activity / mute / the volume slider. Verified live on 2026-09-03: game audio and voice chat together on a fresh session. Seats on NVST's legacy RTSP mic transport are not supported yet and report that when the mic is enabled. Settings → Audio has a local microphone test either way.
- Session recording - ⌘R captures decode frames and game audio straight off the native pipeline.
The standalone WebRTC streaming backend has been removed. WebRTC.framework still supplies NVST's connection bundle, audio, and parts of rendering, as well as Remote Co-Op's browser and native guest connections.
The current architecture and remaining dependency-removal milestones are documented in docs/StreamTransportArchitecture.md, and the provenance of the vendor-protocol code in docs/PROTOCOL_PROVENANCE.md.
Settings → Video → Frame Pacing decides when a decoded frame is put on screen:
- Balanced - the newest frame at every display refresh.
- Smooth - holds one frame, so two that arrive together are shown a refresh apart. Even motion for about one frame of extra latency.
- Lowest Latency - each frame the instant it decodes, with vsync off. Fastest, but tearing is possible, and macOS drops a variable refresh rate display back to a fixed rate.
- VRR - each frame the instant it decodes, in arrival order, with vsync on, drawn on its own render thread. A variable refresh rate display (Adaptive-Sync, G-SYNC Compatible, ProMotion) then refreshes exactly when the frame arrives instead of on a fixed clock, which keeps motion smooth when the game's frame rate moves around. OpenNOW asks the server for a frame rate just under the display's maximum (116 fps on a 120 Hz display), so the stream stays inside the VRR range; a game that runs at the maximum anyway gets its newest frame shown at each refresh. Capping such a game a few fps under the display's maximum in its own settings keeps every frame. On a fixed-rate display it still never tears. With a mouse, also turn on Raw Mouse Input (Settings → Input → Mouse): without it, macOS's once-per-refresh mouse batches become uneven camera steps, and VRR makes them visible. See Raw Mouse Input.
macOS hands mouse movement to apps about once per screen refresh - roughly 120 batches a second, each a few milliseconds late - however fast the mouse itself reports. In a game running at 120 fps, that turns a steady swipe into uneven camera steps from one frame to the next, which looks like micro-stutter even when every video frame arrives on time. A controller does not have this problem: its stick position is simply read once per frame.
Settings → Input → Mouse → Raw Mouse Input reads the mouse directly and sends each movement as soon as the mouse reports it, up to 1,000 times a second on a gaming mouse. The official client reads the mouse the same way.
- It needs the Input Monitoring permission (System Settings → Privacy & Security → Input Monitoring). Without it, OpenNOW keeps using macOS's mouse events and says so in Settings.
- It skips macOS's pointer speed and acceleration, so aim can feel faster than before, and game menus that draw their own pointer (No Man's Sky's inventory, for example) move at the mouse's full DPI. Turn on Match Mac Pointer Speed, just below it, to keep the raw timing but move as far as your Mac's pointer would, like the official client. Or lower Mouse Sensitivity on the same card, or the game's own sensitivity, to match.
- Trackpads and Apple mice keep going through macOS.
Settings → Audio → Surround Sound picks Auto, Stereo, 5.1 or 7.1. When a stream starts, the server lists the surround layouts it can send; OpenNOW asks for the widest one that fits your choice, your speakers and what macOS can decode, and falls back to stereo when the server offers none. Before, it could ask for 5.1 and then decode only two channels, which made game audio sound muffled and underwater.
- Each channel goes to the speaker your output device names, so centre dialogue comes from the centre and rear effects from the back.
- A stereo output gets a proper stereo mix of the 5.1 stream.
- Recordings, the replay buffer and Remote Co-Op guests always get the stereo mix.
- 7.1 is used when the server offers it; the servers tested so far offer up to 5.1.
- 5.1 and 7.1 need macOS 27 or later. macOS 15–26 has no multistream Opus decoder, so on those versions OpenNOW asks the server for stereo however the setting is set. The setting stays available and takes effect as soon as the Mac is on a macOS that supports it.
- If a server describes a surround layout and then sends stereo anyway, OpenNOW switches that session to stereo by itself rather than playing silence — no trip to Settings needed.
- A 5.1 stream folded onto a stereo output is quieter than a native stereo stream: the fold is scaled so all six full-scale channels still fit without clipping, which costs about 8 dB. Switch Surround Sound to Stereo if you would rather keep the level.
OpenNOW talks to Valve's controllers directly over HID, so you get the pad in your GeForce NOW stream without Steam running in the background.
- Every 2026 variant - wired, BLE, and both dongles - plus the original 2015 controller.
- Haptics, grips, trackpads - rumble feedback, four back grips, and both pads parsed and bindable client-side.
- Gyro and flick stick - the 2026 controller's IMU, decoded in the client: right-stick or mouse output, Grip Sense or latch activation, flick stick, and a live MOTION panel in the tester. Opt-in per profile.
- Visual mapping editor - click any control on the controller diagram and bind it to a gamepad button, a key, a mouse action, or nothing at all.
- Combos on any control - bind a back grip to
B + R2; the modifier lands first, the press follows a beat later, so games read it as a real combo. - Profiles - save as many as you like and switch between them.
- Built-in tester - Settings → Input → Controller Tools shows every button, axis, and pad live; Steam Controllers draw their full shell, other pads a generic one.
- Lizard mode off - the firmware's keyboard/mouse emulation is suppressed so nothing leaks to the desktop.
A Steam Deck-style overlay for logins, chat, and search fields in any GeForce NOW title. The keyboard sends keys through NVST exactly the way a physical keyboard does - UTF-8 text for characters, macOS keycodes for Return/Backspace.
- Dual trackpads each own one half of the grid. Touch a pad to aim, click it (or pull L2/R2) to type the aimed key.
- No trackpads? D-pad or left stick moves the grid cursor; A types, B is Backspace, X is Space, Y toggles Shift, Start presses Enter.
- Overlapping game UI? Tap the ⬍ position key in the bottom bar to flip the keyboard to the top of the screen.
- Steam alone still works as the local-cursor modifier - hold Steam to drive the Mac cursor with the right pad, same as before.
Steam grabs the controller exclusively while it's running. Quit Steam first.
Supported hardware and report formats
| Product ID | Device |
|---|---|
0x1102 |
Steam Controller (2015), wired |
0x1142 |
Steam Controller (2015) wireless dongle |
0x1302 |
Steam Controller (2026, "Triton"), wired |
0x1303 |
Steam Controller (2026), Bluetooth LE |
0x1304 |
Steam Controller (2026) 2.4 GHz dongle ("Proteus") |
0x1305 |
Steam Controller (2026) dongle variant ("Nereid") |
Pipeline
OPN/Stream/SteamControllerHIDMonitor.swiftmatches devices by vendor ID0x28deand the product IDs above, opens them via IOKit HID, disables lizard mode with periodic heartbeats, and streams raw input reports.OPN/Stream/SteamControllerReport.swiftparses each report into aControllerInputSnapshot(buttons, triggers, sticks, trackpads).- Snapshots feed the in-app test screen and, during streaming,
NativeGamepadMonitor, which forwards a standard gamepad subset to the GeForce NOW session. Steam/QAM, back grips, and trackpads are parsed and bindable client-side but not forwarded as raw stream input.
Report layouts - bit/byte mappings verified against Valve's contributions to SDL's HIDAPI drivers:
- Legacy (2015) -
ValveInReport_t-framed packets; buttons across three bytes, trackpads double as stick/D-pad emulation. Reference:SDL_hidapi_steam.c. - Triton (2026) - report IDs
0x42(wired/dongle state),0x45(BLE state), and0x47(timestamped state; inserts a 16-bit trackpad timestamp before the pad fields, shifting them by 2 bytes). A 32-bit button mask includes the Steam button (0x0001_0000), Quick Access (0x0000_0010), four back grips, trackpad touch/click bits, and per-pad X/Y plus pressure. Reference:SDL_hidapi_steam_triton.c. - Deck state - report ID
0x09, the Steam Deck-style 64-bit button mask with pads at fixed offsets; used when a device speaks the deck packet format. Reference:SDL_hidapi_steamdeck.c.
Struct layouts for all three formats are documented in SDL's controller_structs.h. Axis values normalize to -1...1 (Int16 full scale), triggers and pad pressure to 0...1. Parsing is covered by Tests/Stream/SteamControllerReportTests.swift.
macOS hands game controllers to apps through Apple's GameController framework, which applies its own dead zone to the thumbsticks before any app sees them, and OpenNOW added a second one on top. Small stick movements never reached the game, and lowering the dead zone in the game's own settings could not bring them back.
Settings → Input → Controller Input → Controller API chooses how pads are read:
- Apple Framework - the default, and the behaviour OpenNOW always had.
- Gamepad API - DualSense, DualShock 4 and Bluetooth Xbox controllers are read from their own HID reports, and their sticks are sent to the game untouched, so only the game's dead zone applies. Any other controller is still read through Apple Framework.
During a stream, the Controller API tile in the ⌘G menu switches between the two and lists each connected controller with the path it is read through and the stick values being sent, so the difference can be felt and checked mid-game. The controller tester in Settings → Input shows the raw values in Gamepad API mode.
⌘R starts and stops a local capture on either transport - frames come straight off the decode path and game audio off the stream, so nothing is re-encoded from the screen and no Screen Recording permission is involved. Recordings land in a browsable library with search, sort, and resolution filters.
Captures are written into OpenNOW's own folders, not NVIDIA's: screenshots go to ~/Pictures/OpenNOW and recordings to ~/Movies/OpenNOW/<Game Title>. Both locations are changeable in Settings → Capture → Storage, where each library has Change…, Reset to Default, and Reveal in Finder. A folder change takes effect immediately for the next capture and for the next library scan; the folders cannot be changed while a stream is running. Upgrading from an older build moves anything already under ~/Pictures/NVIDIA/GeForce NOW or ~/Movies/NVIDIA/GeForce NOW into the new folders on first launch, and says so on the Capture page.
Quick Edit is opt-in and non-destructive - trim the ends, split and cut a middle section, join what is left, then save as a new video. The advanced pass adds crop, rotate, flip, speed, and audio. The original file is never rewritten.
OpenNOW lives in the menu bar, so a session survives its window. The status item is the OpenNOW cloud
- hollow when idle, filled while streaming - and the popover is a compact control surface over whatever you're already doing.
- Session at a glance - the game, what it is doing, and a live elapsed clock, with no window open.
- Resume, Pause, End - Pause tears down the local stream but keeps the cloud seat alive, and Resume rejoins it. A session running on another device shows up here too; the menu re-checks every time you open it.
- Continue Playing - your three most recent games, with box art, ready to launch without opening the window.
Settings → General → Window & Menu Bar decides what happens when the window goes away:
- Menu bar item - show the session in the menu bar, or keep OpenNOW out of it entirely.
- When the last window closes - quit on close, close and keep the Dock icon (the default, so the close button just closes the window and the Dock brings it back), or close and hide from the Dock so only the menu bar item remains.
- Launch at login - start OpenNOW automatically, registered as a macOS Login Item.
- At launch, show - open the main window, or start with only the menu bar item and no window.
The Dock is the other surface over the same state. Right-click the icon for the same three most recent games, plus New Session and Open Recordings, and the icon wears a badge while a seat is queued or waiting to be resumed. A queue wait and a recording export each draw their progress on the tile, so a long wait is visible with the window closed. Choose Close, Menu Bar Only and the app leaves the Dock entirely - there the menu bar is the surface that answers instead.
When GeForce NOW takes a game down for maintenance, the offline notice on its detail page offers Watch. Opt in and OpenNOW watches that one title while it runs and brings you back the moment it is playable again - no checking by hand.
- Per title, opt-in. Nothing is watched implicitly. The control is offered on a game under maintenance and nowhere else; a title that is simply unavailable carries no promise of returning. A watch ends on its own once the title is ready to play, or when you remove it in Settings.
- Told at both edges. Maintenance usually ends into patching, so a returning title first says “is now patching” and later “is ready to play”. The patching announcement hands off to the same queued auto-launch a manual Queue uses, so the game comes up on its own from there.
- Called back, not just told. In the background the Dock icon bounces until you activate OpenNOW and a system notification is posted; with OpenNOW already frontmost there is no bounce to make, so the catalog's status line says it instead. Several titles returning together produce one bounce, not one each.
- Visible the whole time. A watched title is marked in the catalog's rails, its detail notice reads Watching, and the Dock icon wears a badge with how many titles are being watched. The list, with per-title Remove and Stop Watching All, is in Settings → General → Maintenance Watch.
Watching runs only while OpenNOW is running - window hidden and menu-bar-only included - because the check rides the same 30-60 second poll the app already runs for patching. It does not survive quitting OpenNOW; there is no background agent behind it. The vendor publishes no maintenance ETA, so nothing here promises a time: the promise is detection within about a minute of the title actually coming back. Up to 25 titles can be watched at once, and the list is stored on this Mac only - it is never synced to iCloud.
Nine destinations named for what they hold - Account, Video, Audio, Input, Recording, Network, Remote Co-Op, General, and Labs - down a sidebar that shows all of them at once. Settings that arrived recently wear a NEW tag until you have seen them, so a new option in a tab you never open still announces itself.
Search answers the word you already know rather than the one the setting is filed under: type 5.1, black bars, or vsync and it lands on the row, in whichever tab it lives.
xcodebuild build -project OpenNOW.xcodeproj -scheme OpenNOW -configuration Debug -destination platform=macOS CODE_SIGNING_ALLOWED=NORun the package tests from the repository root so SwiftPM uses one shared .build graph:
swift test --scratch-path .build/sharedProject layout, packages, and tooling
Layout
Model- persisted SwiftData models, DTOs, stream value types, and catalog value objectsOPNApp.swift- macOS app entry pointApp- application delegate and app-lifecycle wiringResources- bundled images, fonts, and store icon assetsView- SwiftUI/AppKit views, stream host views, design primitives, and asset catalogsViewModel- observable UI state for login, catalog, controller catalog, and recordingsOPN- authentication, catalog/session services, NVST streaming, Remote Co-Op, telemetry, preferences, logging, and app infrastructureGFN- protocol-specific GeForce NOW clients and wire types (CloudMatch, GDN, Jarvis, LCARS, NesAuth, NetworkTest, NVST, Starfleet, UDS)RemoteCoOp- Remote Co-Op operator notes; the guest page itself ships inResources/RemoteCoOp/browserTests- root SwiftPM test target covering the package-exposed production logic
Packages
The root Package.swift exposes a testable OpenNOW library target over non-app-entry production logic from Model, OPN, and GFN. The Xcode app target compiles all six production directories - those three plus App, View, and ViewModel.
Focused test runs
swift test --scratch-path .build/shared --filter StreamRecording
swift test --scratch-path .build/shared --filter GameServicesTestsAvoid package-local build directories during normal development. Use the root package and shared scratch path so generated SwiftPM state stays in one place and large binary artifacts are not duplicated.
scripts/build/report-spm-build-size.sh # audit generated SwiftPM disk usage
scripts/build/clean-spm-builds.sh # reclaim disk spacePull requests welcome. Use conventional commit prefixes (fix:, feat:, docs:, test:, refactor:, style:, chore:), keep changes focused, and verify the relevant package tests or app build before submitting.








