

Apps, tools, and open-source software.
I build apps, make tools for creative projects, and contribute to Godot. Sometimes that involves ferry schedules. Sometimes it involves making a Nintendo Switch draw pictures.


A live four-color camera inspired by the Game Boy Camera. Adjust contrast and brightness, make your own palettes, or apply the same dithering to photos you already have. Export pixel-perfect images for Game Boy projects or larger copies for your photo library.
App Store
Draws imported images in Tomodachi Life: Living the Dream on Switch 1 and 2, without modifying the console. A desktop converter matches the game’s palette and plans brush strokes and fills. A Raspberry Pi Pico then draws the picture by sending controller inputs over USB.
GitHub

Uses feels-like temperature, rain, and air quality to decide when opening the windows will cool your home. One decision engine powers the iPhone and Apple Watch apps, plus nine widget and complication families that update from a cached forecast.


A compass that points to places you choose, with saved locations synced between iPhone and Apple Watch. Built as a personal tool, it is also used by the Northeast Rail Feed Association to align radio antennas.


Live vessel positions and complete schedules for every Washington State Ferries route, designed for an e-ink display. Each screen is rendered remotely with no client runtime and kept under 100 KiB. The experiment that grew into Aweigh.


Draws the live marine layer over a full-screen map of San Francisco Bay using the HRRR weather model. Optional terrain, neighborhood conditions, and warmest and coolest spots turn changing weather into e-ink cartography.
Godot Engine (opens in a new tab) is a free and open-source engine used to ship 2D and 3D games on every major platform. I have 10 merged pull requests in its core engine, primarily improving Apple platform support across both current and legacy releases.
For eight years, Godot's Windows port reported that daylight saving time was always off. Its timezone API could also return an offset one hour wrong while daylight saving time was active.
OS.get_date() hard-coded its dst field to false on Windows, while OS.get_time_zone_info() ignored the Windows API's separate daylight and standard bias values. Mountain Daylight Time, for example, reported -420 minutes (UTC-7) instead of -360 (UTC-6). The fix selects the active Windows timezone state and applies its corresponding bias.
Godot games on iOS did not receive the pause and resume events needed to save state when the app entered the background. Added those signals on iOS and consistent focus events across iOS and Android.
Wired Godot's MainLoop to UIKit's applicationDidEnterBackground and applicationWillEnterForeground delegates, emitting NOTIFICATION_APPLICATION_PAUSED and NOTIFICATION_APPLICATION_RESUMED. Also added matching focus notifications on iOS and Android. The documentation warns developers that iOS allows roughly five seconds to finish work triggered by the pause signal.
Godot capped iOS exports at 60 frames per second even on ProMotion devices. Added a project setting that lets games use 120Hz without maintaining a custom engine build.
Added display/window/ios/allow_high_refresh_rate, used it to configure CADisplayLink for 120fps, and added CADisableMinimumFrameDurationOnPhone to the exported app's property list. Apple requires both the display-link configuration and property-list opt-in.
Full-screen iOS games need control over the status bar and system-edge gestures. Exposed both as Godot project settings, including the two-swipe protection that prevents players from leaving a game accidentally.
Added display/window/ios/hide_status_bar and display/window/ios/suppress_ui_gesture. The iOS view controller reads them through prefersStatusBarHidden and preferredScreenEdgesDeferringSystemGestures, replacing behavior that had previously been hard-coded in the engine.
iOS can reduce a 120Hz display to 60Hz in Low Power Mode, but Godot kept reporting the hardware maximum. Corrected the value so games could pace rendering against the screen's active limit.
DisplayServer.screen_get_refresh_rate() returned UIScreen.maximumFramesPerSecond unconditionally. The iOS display server now checks NSProcessInfo.lowPowerModeEnabled and reports 60Hz while the system limit is active.
Combining two ordinary display settings could make Godot's built-in recorder export the wrong resolution and aspect ratio. Fixed it to record the project's viewport instead of the operating-system window.
MovieWriter::begin() initialized its output with the window dimensions even though Viewport stretch mode renders at the configured viewport size. It now selects viewport_width and viewport_height before calling write_begin(), so recorded output matches the game's render target.
Pull request #68189 (opens in a new tab) Pull request #68190 (opens in a new tab) Pull request #85229 (opens in a new tab) Pull request #85230 (opens in a new tab)
Many teams still ship on Godot 3.x and cannot adopt a new engine major version just to gain platform support. Backported four iOS improvements so those projects received the same features and correctness fixes without migrating.
Ported the interface controls, ProMotion support, lifecycle notifications, and Low Power Mode refresh-rate fix to the older OSIPhone and display-layer architecture. The four backports were merged into Godot's maintained 3.x branch.
macOS and Windows tool
Plug in a Tesla USB drive and browse six synchronized camera feeds by date, location, and event.
TRMNL plugin
When to open your windows, on an e-ink display. Same decision engine as the iOS app.
Web tool
A browser tool built to trace Aweigh's coastline polygons and export them as GeoJSON.
Try it, GeoJSON Polygon DrawerDesktop only
iOS, iPadOS, macOS, and visionOS app
Resurfaces forgotten Live Photos and videos as a continuously moving gallery.
iOS experiment
A real pizza order, tracked in the Dynamic Island.
GitHub, Live Activity for Dominos Pizza (opens in a new tab)
Godot experiment
Encodes a 32×32, 16-color design in 342 Unicode characters, fitting five designs into one Discord message instead of one.