skills/serve-sim-apple-simulator/SKILL.md
Host Apple iOS Simulators in the browser with MJPEG streaming, touch/gesture control, and AI agent integration via npx serve-sim.
npx skillsauth add aradotso/trending-skills serve-sim-apple-simulatorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Skill by ara.so — Daily 2026 Skills collection.
serve-sim is the npx serve of Apple Simulators. It spawns a Swift helper that captures a booted iOS Simulator's framebuffer via simctl io, exposes it as an MJPEG stream + WebSocket control channel, and serves a React preview UI in your browser. Works with any booted iOS Simulator — no Xcode plugin, no app instrumentation required.
xcrun simctl must be available)npx)# Start preview server (auto-detects booted simulator)
npx serve-sim
# → Preview at http://localhost:3200
serve-sim [device...] # Start preview server (default: localhost:3200)
serve-sim --no-preview [device...] # Stream only, no web UI
serve-sim gesture '<json>' [-d udid] # Send a touch gesture
serve-sim button [name] [-d udid] # Send a button press (default: home)
serve-sim rotate <orientation> [-d udid]
serve-sim ca-debug <option> <on|off> [-d udid]
serve-sim memory-warning [-d udid] # Simulate a memory warning
# Options
-p, --port <port> Starting port (preview default: 3200, stream default: 3100)
-d, --detach Spawn helper and exit (daemon mode)
-q, --quiet JSON-only output
--no-preview Skip the web UI; stream in foreground only
--list [device] List running streams
--kill [device] Kill running stream(s)
# Target a specific device by name
serve-sim "iPhone 16 Pro"
# Start on a custom port
serve-sim -p 4000
# Start as background daemon, returns JSON with stream info
serve-sim --detach
# List all running streams
serve-sim --list
# Kill all running helpers
serve-sim --kill
# Send home button press
serve-sim button home -d <udid>
# Rotate simulator
serve-sim rotate landscape_left -d <udid>
# orientations: portrait | portrait_upside_down | landscape_left | landscape_right
# Toggle CoreAnimation debug flags
serve-sim ca-debug slow-animations on -d <udid>
# options: blended | copies | misaligned | offscreen | slow-animations
# Simulate memory warning
serve-sim memory-warning -d <udid>
# Multiple simulators at once
serve-sim "iPhone 16 Pro" "iPad Pro"
# Send a touch gesture (JSON format)
serve-sim gesture '{"type":"tap","x":200,"y":400}' -d <udid>
┌──────────────┐ simctl io ┌─────────────────┐ MJPEG / WS ┌─────────┐
│ iOS Simulator│ ────────────► │ serve-sim-bin │ ───────────► │ Browser │
└──────────────┘ (Swift) │ (per-device) │ └─────────┘
└─────────────────┘
▲
state file in
$TMPDIR/serve-sim/
▲
┌──────────────────┐
│ serve-sim CLI / │
│ middleware │
└──────────────────┘
State files are written to $TMPDIR/serve-sim/. The Swift binary is bundled in the npm package — no separate Xcode build needed at runtime.
Create .claude/launch.json in your project root:
{
"version": "0.0.1",
"configurations": [
{
"name": "ios",
"runtimeExecutable": "npx",
"runtimeArgs": ["serve-sim"],
"port": 3200
}
]
}
Claude will automatically start the simulator preview when you open the project.
Customize metro.config.js to embed serve-sim at http://localhost:8081/.sim:
// metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const connect = require("connect");
const { simMiddleware } = require("serve-sim/middleware");
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
config.server = config.server || {};
const originalEnhanceMiddleware = config.server.enhanceMiddleware;
config.server.enhanceMiddleware = (metroMiddleware, server) => {
const middleware = originalEnhanceMiddleware
? originalEnhanceMiddleware(metroMiddleware, server)
: metroMiddleware;
const app = connect();
app.use(simMiddleware({ basePath: "/.sim" }));
app.use(middleware);
return app;
};
module.exports = config;
Then run npx expo start — simulator preview available at http://localhost:8081/.sim.
import express from "express";
import { simMiddleware } from "serve-sim/middleware";
const app = express();
// First start the helper in daemon mode
// $ npx serve-sim --detach
app.use(simMiddleware({ basePath: "/.sim" }));
app.listen(3000, () => {
console.log("Dev server at http://localhost:3000");
console.log("Simulator preview at http://localhost:3000/.sim");
});
// vite.config.ts
import { defineConfig } from "vite";
import { simMiddleware } from "serve-sim/middleware";
export default defineConfig({
server: {
middlewareMode: false,
},
plugins: [
{
name: "serve-sim",
configureServer(server) {
// Start helper first: npx serve-sim --detach
server.middlewares.use("/.sim", simMiddleware({ basePath: "/.sim" }));
},
},
],
});
// server.ts
import { createServer } from "http";
import { parse } from "url";
import next from "next";
import { simMiddleware } from "serve-sim/middleware";
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const simHandler = simMiddleware({ basePath: "/.sim" });
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url!, true);
if (parsedUrl.pathname?.startsWith("/.sim")) {
return simHandler(req, res, () => handle(req, res, parsedUrl));
}
handle(req, res, parsedUrl);
}).listen(3000);
});
import { simMiddleware } from "serve-sim/middleware";
// Mount options
simMiddleware({
basePath: "/.sim", // URL prefix for all serve-sim routes
});
// Middleware exposes:
// GET /.sim → Preview HTML UI
// GET /.sim/api → State JSON (connected devices, stream URLs)
// GET /.sim/logs → SSE log stream from simulator
The middleware reads state from $TMPDIR/serve-sim/ and proxies the browser to the live MJPEG + WebSocket endpoints. CORS is open on the helper, so no additional proxy config is needed.
Use --detach to start the helper as a background process and get JSON output for scripting:
# Start daemon and capture JSON output
STREAM_INFO=$(npx serve-sim --detach --quiet)
echo $STREAM_INFO
# {"udid":"...","mjpeg":"http://localhost:3100/stream","ws":"ws://localhost:3100/control"}
# List running streams as JSON
npx serve-sim --list --quiet
# Kill specific device stream
npx serve-sim --kill "iPhone 16 Pro"
# Kill all streams
npx serve-sim --kill
import { simMiddleware } from "serve-sim/middleware";
import connect from "connect";
const app = connect();
// Attach middleware — requires serve-sim helper already running (--detach)
app.use(
simMiddleware({
basePath: "/.sim",
})
);
export default app;
When using serve-sim gesture '<json>':
# Tap at coordinates
serve-sim gesture '{"type":"tap","x":200,"y":400}' -d <udid>
# Swipe gesture
serve-sim gesture '{"type":"swipe","startX":200,"startY":800,"endX":200,"endY":200,"duration":0.3}' -d <udid>
git clone https://github.com/EvanBacon/serve-sim
cd serve-sim
bun install
# Build JS bundles
bun run --filter serve-sim build
# Rebuild Swift helper binary
bun run --filter serve-sim build:swift
# Watch mode for development
bun run --filter serve-sim dev
No simulator detected
# Check booted simulators
xcrun simctl list devices booted
# Boot a simulator if none running
xcrun simctl boot "iPhone 16 Pro"
Port already in use
# Use a different port
serve-sim -p 4200
# Kill existing helpers first
serve-sim --kill
Stream not appearing in middleware
# Ensure helper is running first
serve-sim --detach
# Verify state files exist
ls $TMPDIR/serve-sim/
# Check running streams
serve-sim --list
Swift binary not found / won't execute
# Ensure Xcode CLT are installed
xcode-select --install
# Verify simctl works
xcrun simctl list
Multiple simulators — wrong device targeted
# List all booted simulators with UDIDs
xcrun simctl list devices booted
# Target by UDID explicitly
serve-sim -d <udid>
Rebuild Swift helper if binary is stale
bun run --filter serve-sim build:swift
development
```markdown --- name: compose-performance-skills description: Install and use the skydoves/compose-performance-skills agent skill library to diagnose and fix Jetpack Compose performance issues including stability, recomposition, lazy layouts, modifiers, side effects, and build configuration. triggers: - "my composable recomposes too often" - "LazyColumn drops frames during scroll" - "diagnose Compose stability issues" - "fix unnecessary recomposition in Jetpack Compose" - "optimize Com
development
Headless iOS Simulator manager with host-side HID input injection, 60fps streaming, and device farm web UI for iOS 26
development
```markdown --- name: claude-code-game-studios description: Turn Claude Code into a full 49-agent game dev studio with 72 workflow skills, automated hooks, and a real studio hierarchy for Godot, Unity, and Unreal projects. triggers: - "set up claude code game studios" - "use ai agents for game development" - "set up game dev studio with claude" - "add game studio agents to my project" - "how do I use claude code for game dev" - "set up godot unity unreal ai workflow" - "49 agents g
development
```markdown --- name: xq-py-quantum-vm description: Python implementation of the Quip Network's quantum virtual machine (xqvm) triggers: - quantum virtual machine python - xqvm quip network - quantum circuit simulation python - xq-py quantum vm - quip network quantum python - simulate quantum gates python - quantum vm xqvm - xqvm-py quantum circuit --- # xq-py Quantum Virtual Machine > Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection. `xqvm-py` is a Python impl