Creator & UI Technical Designer
A survival-wave FPS built as a showcase for a fully data-driven, animated night-ops HUD — every element bound to live game state, on a locked design system. Solo, in Unreal Engine 5.
HOLDOUT is a wave-survival first-person shooter I built solo as a focused portfolio piece — but the gameplay is a deliberately lightweight backdrop. The real deliverable is the HUD: a fully data-driven, animated, night-ops interface that reacts to live game state, built on a locked design system and a clean, push-based data architecture. I chose survival-wave specifically because it generates constant, legible state changes — wave number, hostiles remaining, kills, countdowns, damage direction — so every element on screen has something real to react to, bound to a gameplay source rather than faked.
The whole interface runs on a small, strictly-enforced token system. The governing rule is one meaning per colour — the discipline that lets it stay readable and coherent across eight screens and a live gameplay HUD.
Teal is interactive, positive, active. Red is danger, death, incoming threat. Everything else is a neutral grayscale hierarchy — white for primary values, two grays for labels and secondary text, near-black for panels and background. Nothing on screen picks up a colour it hasn’t earned.
Bahnschrift throughout — uppercase, letter-spaced — on a single type scale that governs everything from the logo down to a HUD label. Colour is stored in one convention so tokens render identically everywhere, with two documented, deliberate exceptions: HUD labels and teal are stored slightly brighter, because over-gameplay text needs more contrast than text on a flat dark menu. Knowing when to break your own system, and documenting why, is part of the craft.


The HUD is built on a push-based data flow, not per-frame polling. Gameplay pushes state; the interface listens. It never reaches into gameplay, and it carries no Tick cost.
Gameplay systems — the character, the game mode, the projectile — push state through a single Blueprint interface on the player controller, which routes to Receive functions on the HUD widget. Systems only push when something actually changes, so the HUD stays fully decoupled and any widget can subscribe without polling every frame.
Health lerps on a receive-health call; ammo on receive-bullets; the wave counter and banner are driven live from the game mode; the hostile count decrements on the kill-hook with a red flash; the damage arc rotates toward the attacker and fades over 1.2s; the compass reads live controller yaw. Nothing is faked — each readout is bound to a real gameplay signal.

One small state machine in the game mode drives the whole loop — and four separate UI moments — from a single source of truth.
The wave manager tracks state as plain integers — current wave, enemies remaining — spawning escalating waves and timing the gaps between them. Rather than tracking a list of enemy references, I hooked enemy-death decrementing directly into the existing scoring signal, so the single “an enemy died” event drives both the score and the HUD.
That one state machine drives the wave counter in the HUD, the incoming banner on wave start, the cleared countdown between waves, and the wave-reached stat on the death screen — so every wave-related surface always agrees, because they all read the same numbers.

The animation language is Battlefield-restrained: fast, mechanical, hard-settling, with no bounce or overshoot. Everything runs on a math-based ease-out driven straight from the Blueprint graph.
The signature piece is the wave-banner assembly. Instead of a single fade, the elements cascade in over roughly 0.4s: the plate wipes in, the teal rules snap outward, the “INCOMING” label drops in, then the wave number slams to a hard stop. That staggered choreography is what makes it read as engineered rather than faded.
The same fast-in, hard-settle ease-out runs through the HUD boot-up sequence, the hitmarker, the countdown punch, and the per-kill ticks. Every overlay settles with the same weight, so the interface feels authored by one hand.

The parts I am proudest of are the UI-technical judgment calls — where a real constraint met a reasoned answer.
The HUD washed out over the bright arena. I tested three fixes — an edge scrim, per-glyph text shadows, and a solid backing plate — and chose the plate for overlays plus a corner vignette and lighter labels for the HUD, because they read over any background without occluding gameplay. Diegetic-over-gameworld UI needs a different treatment than menu UI.
For the damage indicator I chose a thin segmented bracket-arc over a big glowing wedge, so it read as part of my HUD rather than borrowed from another game. And when the template’s bright orange arena fought the dark interface, I recoloured the environment to a desaturated night-ops palette specifically so the UI would read — treating art direction as being in service of the interface.

A data-driven HUD with a clean separation between gameplay and UI, pushed through one interface with no Tick cost, is a foundation that scales. The motion language exists for feel, not decoration — every element earns its animation.
A locked token system applied consistently across a full product, with every deliberate exception written down. Repeated consistency audits kept it honest, and the result reads as one product across every screen.
The judgment I value most here is knowing when to break my own system on purpose — brightening HUD labels over gameplay, bending the environment to serve the interface — and documenting exactly why. That is the UI-technical mindset the whole piece was built to show.
Feral