Model or dataset
EvanBacon/serve-sim avatar
EvanBacon/serve-sim

serve-sim: a local HTTP server for a booted iOS Simulator

The `npx serve` of Apple Simulators.

2,792 stars148 forksTypeScriptApache-2.0

At a glance

What is it?
EvanBacon/serve-sim turns a running Apple Simulator into an MJPEG or H.264 stream with a WebSocket control channel and a React preview UI, aimed at agent tools and remote testing. It is arm64-only, needs macOS with Xcode command line tools, and its keyboard path on Xcode 27 depends on Device Hub being frontmost.
Who is it for?
Adopt serve-sim if you are on an Apple Silicon Mac with Xcode command line tools and Node 20 or newer, and you want a booted simulator reachable from a browser or an agent tool without adding instrumentation to your app. Skip it if you are on Intel, if you need a supported release rather than a moving main branch, or if your workflow depends on reliable keyboard input under Xcode 27 without granting Accessibility permission to the launching terminal.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap serve-sim fills between simctl and a browser

Apple ships simctl for driving a simulator from the command line, and Xcode ships a windowed simulator you can look at. Neither gives you a URL. serve-sim exists to close that gap: it spawns a Swift helper that captures the simulator framebuffer through simctl io, exposes it as an MJPEG stream plus a WebSocket control channel, and serves a React preview UI on top. The README states it works with any booted iOS Simulator, with no Xcode plugin and no instrumentation inside your app.

The target user is narrower than "iOS developer". The README names Codex, Cursor and Claude Desktop, and the topics list agent, headless and ios. If you are building a browser-use MCP tool that needs to see and touch a simulator, or you want to test hosted simulator infrastructure locally before paying for a remote Mac, this is the shape of tool you are looking for. The author notes he develops the Expo framework but that the tool is agnostic to React Native. That matters: nothing in the mechanism depends on your app being JavaScript.

Framebuffer capture, a WebSocket channel, and what actually crosses the wire

The data flow described in the README is short enough to hold in your head. serve-sim starts a small Swift helper. That helper calls simctl io to read the simulator framebuffer. It republishes the frames as an MJPEG stream and opens a WebSocket control channel alongside it. A React preview UI is served on top of both, defaulting to port 3200, with the helper defaulting to port 3100.

Control flows the other way over the WebSocket. The CLI can send gestures, button presses, typed text, rotations, CoreAnimation debug toggles and simulated memory warnings, all through the same helper. Logs flow back out: the README says simulator logs are forwarded to the browser so browser-use MCP tools can read them, and recent simulator actions are visible in the browser tools panel and through serve-sim event-log. That event log is the part worth noticing. An agent that can read a list of recent actions can reason about what it just did, rather than guessing from pixels alone.

The codec is selectable. The --codec flag takes auto or mjpeg, where auto means H.264 when the browser can decode it and mjpeg forces software JPEG for cases like VMs without H.264 encode. The README claims full 60 FPS video in the browser. I have not measured that and cannot confirm it; treat it as the project's own description of intended behaviour, not a benchmark.

Getting a stream running: install constraints and the first commands

The install section is where most of the caveats live. You need macOS with Xcode command line tools so xcrun simctl exists, and a maintained Node.js LTS release, currently Node 20 or newer. Older or end-of-life Node versions are explicitly unsupported. The README states that bun is not required to run the CLI, which is worth flagging because the tool ships through npx and people assume a Bun dependency.

The hard constraint is architecture. Apple Silicon (arm64) only. The bundled serve-sim-bin helper ships as an arm64 binary and does not run on Intel Macs. There is no Rosetta path described. Camera injection additionally uses a host-side helper built for macOS 14 or newer.

With a simulator booted, the shortest path is a single command:

npx serve-sim

The README gives the expected output as a preview URL on http://localhost:3200. To target a specific device, pass its name: serve-sim "iPhone 16 Pro". To run the helper in the background and get JSON back, use serve-sim --detach. To see what is already running, serve-sim --list, and to stop everything, serve-sim --kill. Useful startup flags include --panes devices,tools to open specific panes, --fit to size the simulator to the preview viewport, and --theme dark to set the simulator appearance before the preview opens. If you only want the stream and no web UI, --no-preview streams in the foreground.

Driving the device: gestures, typing and camera injection from the CLI

Once a helper is running, the CLI is the control surface. serve-sim button sends a button press and defaults to home. serve-sim gesture takes a JSON payload. serve-sim rotate accepts portrait, portrait_upside_down, landscape_left or landscape_right. serve-sim type sends text through the simulator keyboard, with --stdin and --file variants for piping content in. The README notes typing is US keyboard only. serve-sim ca-debug toggles CoreAnimation flags (blended, copies, misaligned, offscreen, slow-animations), and serve-sim memory-warning simulates a memory warning. Most of these accept -d udid to pick a device.

Camera injection is the most elaborate subcommand. serve-sim camera <bundle-id> injects a synthetic feed and relaunches the app. Sources are a placeholder animation, a live host webcam via --webcam, or a file via --file, where the kind is auto-detected from extension or magic bytes and videos loop at native FPS. Mirroring defaults to auto, meaning front cameras mirror and back cameras do not, and the README notes data-output buffers are never auto-mirrored, matching AVF defaults. You can override with --mirror on|off|auto or the --no-mirror shortcut. The interesting part is hot-swapping: serve-sim camera switch placeholder, switch webcam, or switch to a file path changes the source on a running helper without relaunching the app. serve-sim camera status prints helper state as JSON, and --build rebuilds the dylib and helper from source.

Where serve-sim breaks: arm64, Xcode 27 keyboard input, and no releases

Three limitations are documented, and they are not cosmetic.

First, the arm64 binary. If your team still has Intel Macs in the loop, or your CI fleet does, serve-sim simply will not start there. There is no stated fallback.

Second, keyboard input on Xcode 27. The README says Device Hub must be running with the target simulator window visible and frontmost, and macOS may additionally require the app that launched serve-sim, for example Terminal, to be enabled in System Settings, Privacy and Security, Accessibility. Xcode 26 and older keep using the legacy simulator HID path. You can opt out of the Xcode 27 bridge entirely with SERVE_SIM_DISABLE_DEVICE_HUB_KEYBOARD=1. This is a real operational dependency: a headless agent on a remote Mac has no frontmost Device Hub window, so typed input on Xcode 27 is the feature most likely to fail in exactly the deployment the README advertises. The gesture and button paths are not described as having the same constraint.

Third, there are no retrieved releases. The repository has a main branch and a last push date, but no tagged release in the material I have. That means npx serve-sim resolves against whatever is published, and there is no changelog to read before upgrading. For a tool an agent pipeline depends on, that is a reason to pin a version rather than float.

One more boundary: the README does not describe authentication on the preview server or the WebSocket channel. It advertises LAN hosting and tunnelling a served URL. Anyone who can reach the port can presumably drive the device. I cannot confirm the absence of auth from the material, so verify it yourself before exposing a tunnel.

How this differs from running Xcode's Simulator or a device cloud

The obvious alternative is Xcode's own Simulator window, which renders the device and accepts input but has no HTTP surface, no event log, and nothing an MCP tool can attach to. The second alternative is a hosted device farm or a remote Mac with a screen-sharing session. Those give you a remote device, but the interaction model is a human looking at a desktop, not a program reading a stream and posting gestures to a WebSocket.

The difference in approach is where the control channel lives. A screen-sharing or VNC setup ships pixels and raw input events, and any semantic layer (this tap was a home button, this was a rotation) has to be reconstructed downstream. serve-sim puts semantics in the CLI: button, rotate, type, ca-debug, memory-warning, event-log. An agent calls a named operation rather than synthesizing coordinates. The trade-off is that you are limited to the operations the CLI exposes. If you need a gesture the gesture JSON schema does not cover, or an input path Xcode 27 has moved behind Device Hub, the abstraction is in your way. VNC would still work there; serve-sim might not.

Maintenance, licence and what upgrading actually costs

serve-sim is Apache-2.0. That is a permissive licence with an explicit patent grant and a requirement to preserve notices; it is not copyleft, so embedding the CLI in a commercial internal pipeline does not oblige you to publish your own code. This is a description of the licence text, not legal advice, and the bundled serve-sim-bin helper is a separate compiled artifact whose own notices you should check before redistributing it.

The maintenance picture is unusual. The README documents a source-rebuild path (serve-sim camera --build rebuilds the dylib and helper from source), which means the project expects users to be able to compile the Swift side themselves. That is a hedge against the arm64 binary going stale, and it also means an upgrade can involve a Swift toolchain, not just a new npm version. Budget for that if you pin the package.

The ongoing cost is mostly environmental, not code. Node 20 or newer, macOS 14 or newer for camera injection, arm64 hardware, Xcode command line tools, and on Xcode 27 an Accessibility grant for whichever process launches serve-sim. Each of those is a thing that can change under you on a machine upgrade. The absence of tagged releases means you will not get a changelog telling you which one changed.

Editorial conclusion

Adopt serve-sim if you are on an Apple Silicon Mac with Xcode command line tools and Node 20 or newer, and you want a booted simulator reachable from a browser or an agent tool without adding instrumentation to your app. Skip it if you are on Intel, if you need a supported release rather than a moving main branch, or if your workflow depends on reliable keyboard input under Xcode 27 without granting Accessibility permission to the launching terminal. Before committing, run npx serve-sim --list against a booted device to confirm the arm64 helper spawns and the port range is free, then check serve-sim camera status to see whether the camera helper builds and reports alive on your machine.

Official sources

  1. EvanBacon/serve-sim on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
Community notes

Community notes