Hollow Souls — a solo development studio

Solo development studio

Real-time systems, tools and interactive work — built mostly in Unreal Engine, and mostly on my own.

About

I am a bit of a lost soul.

A career IT professional by trade, a creative at heart, and never quite settled on one or the other. I drift from thing to thing, endlessly curious and hopelessly distracted. The work below is what is left when something holds my attention long enough to finish.

Unreal Engine is the thing I keep coming back to. Five years of it, mostly on systems and tools rather than finished games, and two of them are on Fab: a replicated, Blueprint-only lighting system and a prefab plugin for the editor. Around that, Houdini, Blender and shaders, whenever a problem wanted them.

The rest wanders further: a native macOS app in Swift, a data dashboard in TypeScript, an AWS estate declared end to end in Terraform. Different tools, same habit of following whatever caught my eye.

  • Unreal EngineReal-time systems, editor tools, gameplay
  • BlueprintSystems, replication and rapid iteration
  • BlenderModelling and asset work
  • C++Engine and editor plugins
  • HLSL / GLSLShaders and post-processing
  • TypeScriptWeb apps, data pipelines and this site
  • HoudiniProcedural generation and simulation
  • AWS · TerraformServerless infrastructure, as code
  • Autodesk FusionCAD and fabrication
  • SubstanceMaterials and texturing
  • SwiftNative macOS apps
  • PythonLambdas and editor automation

Work

Real-time

  • An Unreal Engine asset package for building a lighting system out of the thing that actually controls it: the switch on the wall. Switch plates carry up to four switches, each one operates independently, and each can be wired to as many light actors as a room needs — across different light and component types, the way a real fixture rarely matches its neighbour.

    Lights are made by subclassing one parent Blueprint, which tags them automatically; anything a light should do when it changes state hangs off a single event. That is the whole integration. The interaction side ships as an example first-person character with a post-process outline, so aiming at a switch highlights it before you press anything.

    Version 2.0, for Unreal Engine 5.8, makes it multiplayer. The server owns every light's state and replicates it to every client, late joiners included; a client's input goes through a reliable server RPC, and switch levers animate locally from the replicated state, so every machine lands on the same picture. Lights sit dormant until they change, so an idle room costs no bandwidth. It runs under a listen server, a dedicated server or standalone, and it is still Blueprint only.

    Engine versions before 5.8 receive version 1.0, which is single-player only. Originally sold on the Unreal Engine Marketplace, and now on Fab following its closure.

    Stills

    • Switch plates in two classic types, each carrying up to four switches
    • Every switch drives multiple light actors, of multiple light types
    • Network replication in v2.0: server-owned light state, synced to late joiners
    • Listen server, dedicated server and standalone play from the same content
    • Interaction system with a post-process outline on the aimed-at switch
    • Parent Blueprint for building your own compatible environment lights
    • A state-change event and dispatcher for anything a light should do besides turn on
    • Blueprints organised and commented rather than left as a black box
    • Example lights and a first-person character included as reference
    Engine
    UE 5.8 (v2.0) · earlier (v1.0)
    Type
    Blueprint asset pack
    Networking
    Replicated (v2.0)
    Platforms
    Windows · Mac (v1.0)
    Contents
    8 Blueprints · 25 meshes · 54 textures
    Status
    Shipped · v2.0
    Year
    2022
  • An editor plugin that gives Unreal Engine 5.7 and 5.8 a prefab workflow. Select actors, make a prefab, place it as many times as the level needs, then change one instance or all of them. Prefabs nest inside prefabs, with cycles detected and refused, and edit in place: Edit, Commit and Discard, where a commit updates every loaded instance in one undoable transaction.

    Any property on a single instance can be overridden, and the Details panel puts Revert to Prefab and Apply to Prefab on the overridden rows — applying to the prefab itself, to a variant parent or to a nested prefab. A variant is a prefab based on another that stores only its differences, and variants of variants are allowed. Pack turns identical static meshes into ISM or HISM components and estimates the draw calls before it changes anything: a thousand identical cubes go from a thousand draw calls to one, and Unpack puts every node back.

    Built for World Partition rather than despite it. An instance is one actor, so one instance is one external actor file; instances in unloaded cells are found through actor descriptors without loading them, and a commandlet re-syncs them in bulk with dry-run, verify and check-out modes for CI. A Python API covers the same ground, and the plugin ships its own regression suite and a packaged-build smoke test.

    Nothing here locks a project in. Bake exports an instance to plain actors, a Level Instance or a Packed Level Actor, and baking is permanently free. Workflow inspired by Unity prefabs and prefab variants; not affiliated with or endorsed by Unity Technologies.

    Stills

    • Prefabs from selected actors, with a choice of pivot
    • Nested prefabs with cycle detection, and variants that store only deltas
    • Edit in place with Edit, Commit and Discard, nested prefabs included
    • Per-instance overrides with Apply, Revert, Apply All and Revert All
    • ISM and HISM inside prefabs, and Pack / Unpack with a draw-call estimate
    • Bake to plain actors, a Level Instance or a Packed Level Actor
    • Runtime spawning of prefabs and variants from cooked data, in Blueprint or C++
    • World Partition re-sync commandlet with dry-run, verify and check-out modes
    • Prefab Health report for missing prefabs, broken references and stale overrides
    Engine
    UE 5.7 · 5.8
    Type
    Editor plugin
    Platform
    Win64 editor · Win64 games
    Automation
    Python API · commandlet
    Status
    Shipped · v1.0
    Year
    2026

Software

  • A ground-up rewrite of ShareX for macOS, built entirely in Swift on ScreenCaptureKit, SwiftUI and AppKit. Not a wrapper: it keeps the parts of ShareX's workflow that make it worth using and adopts the platform's own APIs and conventions instead of emulating a Windows app.

    It captures fullscreen, window, monitor, region or a scrolling page; records to H.264, HEVC, WebM, GIF, animated WebP or APNG; annotates, blurs and pixelates; runs 51 image adjustments with importable presets and a beautifier; and uploads through a custom engine that speaks multipart, JSON and XML, with OAuth2, S3, Backblaze B2 and Azure behind it. History is a searchable SQLite database that opens a Windows ShareX history unchanged, and a Windows ShareX backup imports in one step — custom uploaders, hotkeys and history together.

    Split into eleven modules in one Swift package — capture, upload, editor, effects, history, tools and updates each standing alone — with a native messaging host for browsers and a Share Extension so it appears in the system share sheet. GPL-3.0, signed and notarised, installable from Homebrew, and it keeps itself current: updates are checked against the release's published SHA-256 and Developer ID team before the app is replaced in place. The interface ships in seven languages.

    Files ported from the original C# codebase keep the ShareX Team copyright alongside their own. Not affiliated with or endorsed by the ShareX team.

    • Capture fullscreen, window, monitor, region or a scrolling page, with overlay tools
    • Record to H.264, HEVC, WebM, GIF, animated WebP or APNG, with audio
    • Annotate with shapes, text, blur, pixelate and a full undo stack
    • 51 image adjustments and filters, with presets that import and export
    • Upload engine speaking multipart, JSON and XML — OAuth2, S3, B2, Azure
    • SQLite history and settings migration straight from a Windows ShareX backup
    • OCR, QR, colour picker, ruler, converters and a background remover built in
    • Global hotkeys, watch folders, a URL scheme, CLI verbs, browser integration
    • Lands in the macOS Share menu and the right-click Services menu
    • Verified in-place updates, with Homebrew installs left to Homebrew
    Language
    Swift 5.10+
    Platform
    macOS 14 Sonoma+
    Stack
    ScreenCaptureKit · SwiftUI
    Licence
    GPL-3.0
    Status
    Released · active development
    Year
    2026

Cloud

  • Japan publishes its immigration figures faithfully and in a form almost nobody can read: government spreadsheets, hundreds of thousands of rows, no way to ask them a question. This turns them into something you can actually look at — two datasets, thirteen charts, one address.

    The first dataset is application processing across the Regional Immigration Bureaus: volume, completion rates, and how long an office is actually taking. The second is the resident population — roughly 202 nationalities across 43 residence statuses, in half-yearly snapshots back to December 2012. Both filter by office, category and period, and every filter is in the URL, so a view you have set up is a link you can send someone.

    The hard part is upstream of the charts. Official exports arrive with regional totals that already contain their own branch offices, a residence-status hierarchy with the parent relationships wrong or missing, and rollup rows that double-count anything that sums them. A build-time transform deaggregates the branches, repairs the hierarchy, and drops the rollups and the zero rows, taking the resident table from roughly 191,000 rows to about 42,000 — then re-adds what it kept against the official totals and fails the build on any mismatch, so a chart can never look fine and be quietly wrong. A scheduled job probes the e-Stat API daily and only pulls and redeploys when the published survey date actually moves.

    It runs in twelve languages, exports what it draws as CSV, and ships a plain data table alongside the charts so the content does not depend on being able to see an SVG.

    • Two datasets — application processing and resident population — filtered independently
    • Thirteen charts: Sankey flows, zoomable treemap, sunburst, ranked lollipop, two choropleths
    • Processing Time Estimator that shows its working in LaTeX and tracks queue position
    • Every filter and chart setting lives in the URL, so any view is a shareable link
    • Daily watcher that probes the official API and only deploys when the data moved
    • Build-time transform that deaggregates, repairs the hierarchy and checks itself against official totals
    • Policy events marked on the timeline, each linked to the government page that dates it
    • Twelve locales, with three width variants per label so names never truncate
    • CSV export and an accessible data table as the text alternative to the charts
    Stack
    Next.js 15 · React 19
    Language
    TypeScript, strict
    Data
    e-Stat API · GitHub Actions
    Locales
    12
    Licence
    MIT
    Status
    Live · v1.6.5
    Year
    2025
  • The Cloud Resume Challenge sets objectives rather than steps — it tells you what the thing must do and leaves the architecture entirely to you. Taken seriously, that is the point: there is no tutorial to follow and no correct answer to check against, so every decision is one you have to be able to defend.

    What came out of it is a single-page app with an API behind it, standing on twenty AWS services and fully serverless, with no idle compute — identity and keys, DNS, CDN and WAF at the edge, API Gateway and Lambda, S3 and DynamoDB, events and queues and mail, and tracing and audit underneath all of it. None of it is clicked together in a console. The whole estate is declared in Terraform, which means it can be read, reviewed and rebuilt, and the architecture diagram is generated by a script in the same repository rather than drawn, so it is kept in step with the Terraform.

    The CV is compiled to PDF from the same JSON the site renders, so the two can never disagree; a Lambda caches it in S3 by a digest of its content and only renders a new one when that content changes. The download goes through a signed CloudFront URL minted per click, so a download is an event that can be counted rather than a file that vanishes, and the address on the CV is a forwarding alias that SES rotates monthly, so every message shows how old the sender's copy is.

    GitHub Actions gates every pull request on unit tests, type checking, format validation, an end-to-end run, a Terraform plan and Lambda linting; merging applies the reviewed plan, deploys, and smoke-tests what landed — without repeating work the pull request already proved.

    Stills

    • Fully serverless single-page app with an API behind it, on 20 AWS services
    • Every resource declared in Terraform — no console-built infrastructure
    • CV compiled to PDF from the site's own data, cached by content digest
    • Signed CloudFront URLs, so each CV download is a countable event
    • Monthly-rotating SES forwarding alias as the CV's contact address
    • Edge stack of Route 53, CloudFront and WAF in front of S3
    • API Gateway, Lambda and DynamoDB behind it, with events, queues and mail
    • Tracing and audit wired in rather than bolted on afterwards
    • Pull requests gated on tests, types, formatting, e2e and a Terraform plan
    • Merges apply the reviewed plan, deploy to production and smoke-test what landed
    Cloud
    AWS · Terraform
    Frontend
    React · Vite · TypeScript
    Testing
    Vitest · Cypress
    Delivery
    GitHub Actions
    Status
    Live
    Year
    2024

The Forge

Notes from work outside the catalogue above, newest first.

  1. Handoff for UMG

    1.0.0 is in Fab review. It imports a design package into Widget Blueprints, and re-imports as a three-way merge, so graphs, bindings and hand edits survive a design change.

  2. Shipwright

    In progress: an editor plugin that takes a project from Package to a live store branch in one panel. A failed build is matched against 15 data-driven rules, each with the cause and, where it is safe, an undoable fix. Steam, itch.io and Epic uploads still need verifying against real test pages.

  3. BBPC

    Lightmap texels hold the square root of irradiance, so they decode squared; the light probes beside them needed scaling by 1/3500. Found by regressing probes against lightmaps over nine maps.

  4. BBPC

    Windows build, cross-compiled from Linux with llvm-mingw. Runs on the hardware GPU from WSL through the interop.

  5. BBPC

    Cured the Isz chalice glitch on purpose, and started a doc recording every place the port departs from the retail game, and why.

  6. BBPC

    All 10,540 of the game's FXR effect files decode, and the effects simulate and draw natively.

  7. BBPC

    Of 23,913 sound events in the player's animations, 23,866 resolve to an event in the game's data. The other 47 name events that exist in none of its sound projects.

  8. BBPC

    Removed the event-flag store's change log. Nothing read it, so it grew without bound.

  9. BBPC

    Started a clean-room PC port of Bloodborne: C++20 on SDL3 and Vulkan, reading every asset from the player's own copy of the game.

  10. Money Forward toolkit

    Two fixes from running it against the live API: the token endpoint wanted HTTP Basic client auth, and responses send null for fields the spec marks required.

  11. PixelBoard

    Moderation: user reports, admin takedowns and content scanning.

  12. PixelBoard

    An r/place-style 1000×1000 canvas where pixels can be bought and locked by the week. A Postgres exclusion constraint makes overlapping locks impossible, so a double-sell during checkout is caught at the database and refunded.

  13. Money Forward toolkit

    An English toolkit for Money Forward's Japanese-only accounting API: translated OpenAPI spec, typed client, CLI and MCP server, covering all 23 operations.

Contact

If you have something that needs building, or you just want to talk about how something was made — I read everything.

Available for contract and collaboration on real-time work, tooling and technical design.

Protected by reCAPTCHA. Google’s Privacy Policy and Terms of Service apply.