Build1 publisher3 min readPublished
Reading the simulator framebuffer on a damage signal turns an agent's look into a 6.7 ms read
simframe's author rebuilt iOS Simulator perception as an always-on daemon after watching Claude Code take a fresh screenshot before every tap. The study he cites puts 75-94% of task time inside model calls.
The Engineer · Build desk
What happened
- The author watched Claude Code drive the iOS Simulator by taking a screenshot before every tap, with each step paying a network round trip and a few thousand tokens.
- His replacement is a daemon that watches the screen continuously and always holds the newest frame, so the agent's look is a read of current state.
- The first version looped simctl io screenshot, turning a 130 ms blocking call into a 20 ms file read, and the rebuilt daemon reads the simulator framebuffer directly.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint Since model calls hold most of the clock in the study behind this design, a faster capture path saturates fast; the daemon shortens a task only where it removes a call from the loop.
- cost Adopting it means running and supervising a stateful process per simulator plus a screen-memory store, and rewriting tests that assume frames arrive on a schedule.
- capability With the current frame always in hand, an agent can verify a remembered control position locally. Acting on memory becomes checkable.
- decision Teams building UI agents now have a worked alternative to exposing screenshot() as a model tool, and have to decide whether perception sits behind a contract or inside the tool list.
Cheap capture does not buy much on its own. In the OSWorld-Human measurements the author cites, planning and reflection calls to the model take 75-94% of total task time [6]. That leaves between 6% and 25% for capture, taps and the rest of the harness [1]. Cutting one look from a 130 ms blocking screenshot to a 6.7 ms read removes about 123 ms [2], and that saving lands inside the small slice. The 31x only shortens a task where it deletes a model call: screen memory lets the agent act on an intent such as "the Settings tab" without re-reading the screen [15]. Each step carries the whole observation history, so a look that never happens is an image that is never sent in later requests [7].
The unit change is the better idea. The daemon has no frame rate; it captures when the simulator reports a damage signal, about 52 per second while something moves and nothing while the screen is still [13]. Fifty-two per second is one change roughly every 19 ms [3]. A 130 ms blocking screenshot yields about 7.7 captures a second, close to one in seven of those signals [4], so the first version could not have tracked an animation even in principle. The author's CI check asserted that a frame counter had advanced on an idle screen, and it failed three times before he understood why [14].
Inside the 6.7 ms, the capture primitive is about a thousand times faster than a screenshot [12]. Against the post's own 130 ms figure that is on the order of 0.13 ms [5], and almost all the remaining time is the downscale [11]. For anyone copying this, the next thing to optimise is the resize. The 31x for the same work with 6.7 ms left implies a baseline near 208 ms [6], above the 130 ms call the loop version replaced [10].
Two conditions have to hold before those numbers transfer. The environment has to let you read a framebuffer without going through a screenshot command [11], and it has to tell you when the screen changed [13]. simframe gets both from the iOS Simulator. The post does not say what hardware produced the timings.
The author is direct about what he gave up. "I am not as precise as the model. It never mis-taps a control it can see; I do," he wrote [16]. The design's answer is that the newest frame is always held, so checking a remembered control position against the screen is a read [9]. "But I am much faster, and after a while I realised the speed came from architecture, not ability," he wrote [17]. "I ended up with three parts, and I think the vocabulary is more reusable than the code," he wrote [18]. That lands against a field where almost every framework, including the computer-use loops the model labs ship, still treats seeing as a function the model calls [5].
What to watch
- Whether anyone reproduces the damage-signal approach outside the iOS Simulator, where framebuffer access and change notifications come from a different layer.
- Published step counts or wall-clock times for a full task with the daemon in place. That is the measurement that would test the design against the OSWorld-Human baseline.
- Whether the perceive/reason boundary the author calls a contract is actually specified and published, or stays internal to simframe.