Work

Games and systems I’ve built, with a breakdown of the parts I was responsible for.

Portfolio

Client projects and larger builds.

Klaus: Tabula Rasa

A hand drawn 2D adventure. Rebuilt the player as a state machine over component nodes, then added the moves the art had already been drawn for: climbing, vaulting, ladders, crouching, a stomp attack, and a worm that hunts you.

The vault, picked up off the top of a jump. Gravity is off and the body is placed outright, so the solid fence he is passing through does not stop the move half way.
The vault, picked up off the top of a jump. Gravity is off and the body is placed outright, so the solid fence he is passing through does not stop the move half way.
The street outside Arthur's office. Everything is drawn by hand at z zero, which is why depth has to be handled by scripts rather than by layers.
The street outside Arthur's office. Everything is drawn by hand at z zero, which is why depth has to be handled by scripts rather than by layers.
The far side of the same fence. Crossing it reordered the art around him: he is now behind the wall, and the bags and crate leaning on it crossed with it rather than being left standing in front of a man who has gone past.
The far side of the same fence. Crossing it reordered the art around him: he is now behind the wall, and the bags and crate leaning on it crossed with it rather than being left standing in front of a man who has gone past.
On a ladder in the town cutaway. Gravity is off and he is pinned horizontally to the rungs, which is what stops him sliding off a ladder a few pixels wide.
On a ladder in the town cutaway. Gravity is off and he is pinned horizontally to the rungs, which is what stops him sliding off a ladder a few pixels wide.
Shoving the attic object toward the breakable floor. The boards hold him all day and give way under the object, and the hole it leaves is his way down.
Shoving the attic object toward the breakable floor. The boards hold him all day and give way under the object, and the hole it leaves is his way down.
The town interior seen whole, the kind of space a camera zone exists for: the camera pulls out to frame the room and keeps following him inside it without ever showing what is outside.
The town interior seen whole, the kind of space a camera zone exists for: the camera pulls out to frame the room and keeps following him inside it without ever showing what is outside.
Crouched in the street. The collider shortens with him, and the stand up is refused while there is anything overhead.
Crouched in the street. The collider shortens with him, and the stand up is refused while there is anything overhead.
Tools:
Godot 4.7, GDScript, LimboAI (HSM and behaviour trees), Spine 2D, AnimatedSprite2D, CharacterBody2D, Component node architecture
Platforms:
PC
Role:
Gameplay Programmer for Fiverr (@shadetail) · 2026 · Client Delivered

Klaus is the client's own game, an adventure drawn entirely by hand in a loose ink style. I came in with a working prototype and a folder of animation the character had been drawn performing but could not yet do, and my job was the gameplay layer underneath: make Arthur move the way the art says he moves, and give the world enough furniture that a level designer can build with it.

The player was one 352 line script when I started. I took it apart in stages that were each safe on their own: fix the outright bugs first, then restructure without changing a single behaviour, then split the file into component nodes, and only then hang a LimboAI state machine over the top. By the end there is one state per control mode, sitting, walking, frozen for a cutscene, climbing, vaulting, on a ladder, crouched, mid stomp, and the rule that only the locomotion state moves the body, so every other state freezes Arthur simply by not doing that.

The hardest part was not the states, it was the art. The climb and vault clips were each drawn on their own canvas, against whatever obstacle the artist had in mind, with the character wandering around inside the frame. None of that lines up with a real fence in a real level. So I wrote ArtMotion: it measures the drawing's own translation, cancels it frame by frame with a sprite offset, and hands the movement back to the state to reapply against the actual obstacle. The artist's timing and shape survive; the distances come from the level. It also runs the playhead itself rather than letting AnimatedSprite2D do it, because a sprite that advances on the render frame is a coin toss when you read it from physics, and every mismatch is a visible twitch.

The rest is level furniture, written so a designer can place it and walk away: a ladder that is only a collision shape, a fence that also swaps its own art in front of and behind Arthur as he goes over it, a floor that breaks under a pushed object but not under a man, a camera that a level can borrow for an establishing shot or a bounded zone and always gets handed back exactly as it was.

A climb and a vault are the same input off the same jump and differ only in where they end. The climb finishes standing on the obstacle, so it aims at the obstacle's own surface; the vault finishes past it, so it aims at the collider's far edge and at the height the jump started from. Both run with gravity off and the body placed outright, because a solid fence would otherwise stop the move dead half way through.

Nothing in the levels says what is in front of what: everything is drawn at z zero and sorted by tree order. So the fence moves the art around Arthur instead of moving him. The art is what shifts rather than the player, so nothing follows him out of the scene, and more than one node can be listed because a wall is rarely one sprite and the bin bags leaning on it have to cross with it.

The player rebuild was deliberately four commits rather than one. Bugs first, then a restructure with no behaviour change, then the split into components, then the state machine on top. Each step was reviewable on its own, and if the client had stopped after any of them they would still have had a working game, which is not true of a single rewrite.

The breakable floor does not snap. The boards splinter for a per floor number of frames while the object still rests on them, and only then does the collider go. That beat is the whole reason it reads as timber failing rather than a trapdoor opening, and it stays broken across a reload, because a way down Arthur opened should not become scenery that resets behind him.

Credits

Gameplay programming
Bilal AhmadThe player rebuild and state machine, ArtMotion, the enemy behaviour tree, and the level furniture described on this page.
Klaus: Tabula Rasa, the game itself
The clientThe game, its characters, hand drawn art, animation and level design are the client's. The key art on the card is theirs. I was hired for the gameplay layer under it.
Engine addons
LimboAI, Spine RuntimesThird party addons used for the state machines, behaviour trees and skeletal animation, not written by me.

Desvelado on iOS

Took an already released Godot game to iOS: a signed build on TestFlight straight out of cloud CI, a lives economy the client can retune from Firestore, and the packaging fix that finally made Firebase cloud saves work on a real phone.

Out of lives, mid level. The player is offered a rewarded ad for an instant full refill, or can wait out the free one; the countdown underneath is live and keeps running with the app closed.
Out of lives, mid level. The player is offered a rewarded ad for an instant full refill, or can wait out the free one; the countdown underneath is live and keeps running with the app closed.
Straight after the rewarded ad: back to six lives, the counter restored and the refill deadline cleared. The touch HUD around the edges is the mobile layout the same build ships to iOS and Android.
Straight after the rewarded ad: back to six lives, the counter restored and the refill deadline cleared. The touch HUD around the edges is the mobile layout the same build ships to iOS and Android.
The main menu carries the same countdown, so a player who closes the app mid run can see when they will be able to play again. The two store entries sit bottom right.
The main menu carries the same countdown, so a player who closes the app mid run can see when they will be able to play again. The two store entries sit bottom right.
The same popup asking for an ad in exchange for a hint. One popup and one rewarded ad callback serve every offer in the game, told apart by a mode flag.
The same popup asking for an ad in exchange for a hint. One popup and one rewarded ad callback serve every offer in the game, told apart by a mode flag.
A third use of that popup: a rewarded ad for a slowdown power up.
A third use of that popup: a rewarded ad for a slowdown power up.
The slowdown running after the ad completes, with its own duration bar at the top and the world tinted for as long as it lasts.
The slowdown running after the ad completes, with its own duration bar at the top and the world tinted for as long as it lasts.
Tools:
Godot 4.6.3, GDScript, Codemagic CI, App Store Connect API, TestFlight, Firebase Auth, Cloud Firestore, AppLovin MAX, Google Play Billing, Xcode, CocoaPods
Platforms:
iOS, Android
Role:
Release and Systems Programmer for Fiverr (@boronatic) · 2026 · Client Delivered

Desvelado is the client studio's own game, already out on Steam and Android. I was hired for the ring around it rather than the game itself: getting an iOS build out of Godot and onto TestFlight, adding a lives economy the client could tune without shipping a build, and working out why Firebase, in app purchases and ads all behaved in the editor but not on a real device.

Godot cannot produce a signed .ipa without a Mac, so the build runs on Codemagic. The workflow pulls Godot 4.6.3 and its export templates onto a mac mini runner, imports the project headless, exports an Xcode project, runs CocoaPods when Godot emits a Podfile, fetches signing files through an App Store Connect API key, builds a signed .ipa and uploads it to TestFlight. Nothing secret is committed: the .p8 key and a generated RSA key live in a secure Codemagic variable group, and the workflow creates or fetches the distribution certificate itself.

The most valuable hour on the job went into proving where Firebase actually broke. In the editor it signed in against the client's live project and completed a full save round trip. On the phone it printed one line: "Unable to read Firebase config". The addon reads its credentials from a plain .env file, and Godot's exporter only packs files it recognises as resources, so that file never made it into the build. No sign in meant no cloud save, which is exactly why progress looked like it reset on device, on both Android and iOS. Renaming it to firebase.env and adding *.env to the non resource export filters on both presets fixed it without a line of code changing.

The lives system is the one piece of new gameplay. A life is spent on each death; at zero the game offers a rewarded ad that refills to full or a trip back to the menu. Lives also come back on their own against a wall clock deadline written into the save, so the timer keeps running while the app is closed and is checked again the moment the app returns to the foreground. Maximum lives, the refill interval and whether the system is on at all are read from Firestore, so the economy is retuned from a console rather than a release.

The refill deadline is a unix timestamp in the save file, not a running timer, so closing the app does not pause it. On launch and on every application resume the manager compares the deadline against the system clock and tops the player back up if it has passed. A deadline further away than one full interval is treated as a clock that has been wound back and is rewritten, so moving the device clock cannot park the player on a permanent wait.

The game already had a rewarded ad popup for power ups, so the out of lives offer became a mode on that popup rather than a second one. The Yes and No handlers branch once on the mode and hand off, which keeps a single ad callback, a single pause path, and a single place to change if the ad network is ever swapped.

Firebase looked broken and was not. It signed in, read the client's save out of Firestore and wrote one back, all from the editor against their live project. Only the built app failed, and it said why: "Unable to read Firebase config". The addon keeps its credentials in a plain .env file, and Godot's export step copies only files it recognises as game resources. The file was in the project and never in the build. That distinction was worth making carefully, because the visible symptom, progress resetting on the phone, reads like lost work rather than a missing setting.

Lives, ads and purchases are the kind of thing a client wants to move after launch, so none of the numbers are compiled in. GameConfig reads them from Firestore at startup, with the code falling back to sane defaults (six lives, a 24 hour refill) when the document is missing, so a bad or absent config degrades to a playable game instead of a broken one.

Credits

Release pipeline and systems programming
Bilal AhmadThe iOS build pipeline, lives system, touch onboarding, and the Firebase, ads and in app purchase fixes described on this page.
Desvelado, the game itself
The client studioThe game, its art, audio, writing and level design are the client's, and were already released on Steam and Android before I joined. My work sits on top of that build.
Third party integrations
godot-firebase, AppLovin MAX Godot plugin, GodotGooglePlayBilling, GodotPlayGameServicesExisting addons wired into the project and shipped, not written by me.

Procedural Area Generator

A Godot editor tool that turns a small JSON recipe into an editable 3D area. Same recipe and seed always rebuild the exact same scene, so designers can hand-edit and regenerate without losing their work.

A cave area built from one recipe: prefab rooms linked by winding auto-tiled tunnels, every corridor cell classified by its walkable neighbours and given the matching rotated piece (straight, curve, T-junction or cross).
A cave area built from one recipe: prefab rooms linked by winding auto-tiled tunnels, every corridor cell classified by its walkable neighbours and given the matching rotated piece (straight, curve, T-junction or cross).
A larger area from the module kit: whole prefab rooms and floor/wall chambers on a grid, one light rig per room, all baked to an editable scene.
A larger area from the module kit: whole prefab rooms and floor/wall chambers on a grid, one light rig per room, all baked to an editable scene.
Top-down view of the seeded room graph: a spine with kind-dependent branches and a few loop edges, rooms dropped onto a coarse lattice so modules and corridors fit without overlap.
Top-down view of the seeded room graph: a spine with kind-dependent branches and a few loop edges, rooms dropped onto a coarse lattice so modules and corridors fit without overlap.
Variation is picked per placement so repeated rooms and corridors do not read as identical, while the layout for a given seed stays fixed.
Variation is picked per placement so repeated rooms and corridors do not read as identical, while the layout for a given seed stays fixed.
Tools:
Godot 4.7, GDScript, Editor plugin (@tool), Seeded RNG determinism, GLB module kits, Trimesh collision baking
Platforms:
PC
Role:
Tools Programmer for Fiverr (@eutecticstudios) · 2025 · Client Delivered

This is a dev-time area generator that lives inside the Godot editor rather than in the shipped game. A designer picks a short declarative JSON recipe, presses Generate, and a full 3D area is built as a live preview under the open scene: rooms, winding tunnels, stairs, gates, lighting and spawn markers. Saving writes a plain .tscn to disk that any Godot developer can open and edit by hand. The tool never ships; the game ships the baked scene.

The core guarantee the client wanted is determinism. The same recipe plus the same seed produces the exact same scene down to node names and transforms, so a designer can generate a base, inherit a copy, hand-tune it, regenerate the base, and keep every override. All layout randomness flows from a single seeded RNG consumed in a fixed order; a separate hash-of-cell-coordinate source drives per-tile art variety, so adding or removing an asset variant changes only that one tile instead of reshuffling the whole map.

The generator is deliberately asset-agnostic. It references no file paths itself: a kit descriptor is the only place asset paths live, so swapping the kit produces a different-looking area from the same algorithm. That split (recipe says what to build, kit says which assets to build it from) is what lets a future biome reuse the whole pipeline with a new asset pack and only a lighting rig in code.

Determinism is the whole point. Because every layout decision reads from one seeded RNG in a fixed consumption order, seed 7 always yields this exact graph. Insert a random call in the middle and every downstream value shifts, changing the entire map for that seed, so the consumption order is treated as a contract.

Tunnel pieces are authored once, in a single canonical orientation, and declare which edges they open onto (N=-Z, E=+X, S=+Z, W=-X). The generator rotates each in 90-degree steps to match a corridor cell's open neighbours and reserves cells for pieces larger than one tile so nothing tiles underneath them.

The generator touches no asset paths. Kits are the only place files are named, so the same algorithm produces a cave, a dungeon, or any future biome purely by swapping the kit and adding a lighting rig. Empty asset slots fall back to built-in box/cylinder primitives, so a half-finished kit is always runnable.

Credits

Tool programming
Bilal AhmadThe recipe/kit system, deterministic layout pipeline, tunnel auto-tiler, editor plugin, and kit builder described on this page.
Cave art assets
KenneyModular Cave Kit, kenney.nl, released under CC0. Used as the sample module kit; the generator is asset-agnostic and ships no art.

Ultimatum

A 3D party platformer with two ways to play together: Steam lobbies over the network, and up to four players on one screen.

Local split-screen. Each viewport runs its own camera and gamepad binding, independently of the networked path.
Local split-screen. Each viewport runs its own camera and gamepad binding, independently of the networked path.
The Steam lobby browser: hosting with a chosen player cap, refreshing the list, and joining an open lobby.
The Steam lobby browser: hosting with a chosen player cap, refreshing the list, and joining an open lobby.
Character selection. Each player's pick is registered with the host so every client spawns the right mesh for everyone.
Character selection. Each player's pick is registered with the host so every client spawns the right mesh for everyone.
The 3D main menu, splitting local from online.
The 3D main menu, splitting local from online.
Tools:
Godot 4.7, GDScript, GodotSteam, High-level multiplayer RPC, Split-screen viewports
Platforms:
PC
Role:
Multiplayer Programmer for Fiverr (@ethanescamilla) · 2025 · In Development

Ultimatum is a competitive 3D platformer built for playing with other people. It ships two entirely separate multiplayer paths: an online mode built on Steam lobbies with host migration through the lobby list, and a local split-screen mode supporting up to four players on a single machine with gamepads.

The project started from an existing third-person controller asset, so the character movement and camera were not mine to write. What I built is everything that turns a single-player controller into a game two to four people can play at once: the networking layer, the lobby flow, the split-screen viewport handling, and the shared state that keeps everyone's client agreeing on who is who.

The awkward part of any networked character game is that animation state does not replicate for free. Position syncs cheaply; whether a player is grounded and how far through a blend they are does not. I keep an authoritative table of animation state keyed by peer, updated over RPC, so remote players animate correctly on every client instead of sliding around in a T-pose.

Character selection is host-authoritative. A peer announces its pick, the host records it in a peer-to-character map, and spawning reads from that map rather than from each client's local guess. Without a single owner of that state, two players picking at the same moment can end up as the same character on different machines.

Animation state is replicated explicitly. A per-peer table holds the grounded flag and blend amount, pushed over RPC as it changes, because position replication alone leaves remote players animating as though they were standing still.

Online and local are deliberately separate paths rather than one abstracted mode. Split-screen has no peers, no RPCs and no lobby: it has viewports and gamepad assignments. Forcing both through one code path would have meant carrying network concepts into a mode that has none.

Credits

Multiplayer programming
Bilal AhmadSteam networking, lobby flow, split-screen, character sync, and menus: the work described on this page.
Third-person controller
Jeh3noState-machine character controller and camera asset used as the movement base.
Character model
GtiboFrom the Godot Plush Character project, which the controller asset builds on.
Steam integration
GodotSteamSteamworks bindings for Godot.
Input prompts and prototype textures
Kenneykenney.nl, released under CC0.

Tower Defense

A 3D wave-based tower defense where you place defenders by hand into a village under siege: waves are built from designer-facing parameters rather than hand-authored spawn tables.

The village under wave one. Wave number and remaining enemy count are driven by signals from the wave manager rather than polled each frame.
The village under wave one. Wave number and remaining enemy count are driven by signals from the wave manager rather than polled each frame.
The inspect panel reads health, speed, damage, reward and description straight off the enemy's resource: balance data and UI never drift apart.
The inspect panel reads health, speed, damage, reward and description straight off the enemy's resource: balance data and UI never drift apart.
Wave two, mid-surge. Enemy counts scale per wave from a growth multiplier instead of hand-authored spawn tables.
Wave two, mid-surge. Enemy counts scale per wave from a growth multiplier instead of hand-authored spawn tables.
A structure taking damage. Buildings use Voronoi fracture geometry, with burn and damage states as the health value falls.
A structure taking damage. Buildings use Voronoi fracture geometry, with burn and damage states as the health value falls.
Placement highlighting. Hovering a pawn swaps its material so the target reads clearly against a busy village.
Placement highlighting. Hovering a pawn swaps its material so the target reads clearly against a busy village.
Tools:
Godot 4.7, GDScript, Forward+ renderer, Resource-driven data, Voronoi fracture
Platforms:
PC
Role:
Gameplay Programmer for Fiverr (@mallionaire) · 2026 · In Development

A 3D tower defense set in a low-poly village. Enemies enter from rotating spawn points and march on the base; the player answers by dragging defender pawns from an inventory onto the map, inspecting enemy stats mid-fight, and spending the gold that kills return.

The wave system is the core of it. Rather than authoring each wave by hand, waves are generated from a small set of exported parameters: surges per wave, base surge size, a growth multiplier, spawn interval, gap between surges, and how many entrances go live each wave. Changing the pacing of the whole game is a handful of numbers in the inspector, and every stage of the sequence broadcasts a signal so UI and audio can react without polling.

Enemies are Resources rather than hardcoded scenes. Each type carries its own stats, economy values, spawn weighting and description text, which is what lets the inspect panel populate itself from data instead of from a switch statement.

A wave is generated rather than authored by hand. It is defined by a count of surges, a base size, a growth multiplier per wave, an interval between spawns, a gap between surges, and how many entrances go live, all exported to the inspector. Retuning the difficulty curve of the entire game means editing those numbers instead of rewriting spawn tables.

Every stage of the wave sequence emits a signal: wave started with its total, surge started and completed, each individual enemy spawned, the countdown ticking, and the final all-clear. UI, audio and camera work hang off those signals, so nothing polls the wave manager to find out what is happening.

Enemy types are Resources, not scenes with scripts attached. Stats, gold reward, spawn cost, spawn weighting and the description string all live on the resource, which is why the inspect panel can populate itself from whatever it happens to be hovering.

The minigames suspend the main loop through a single global flag rather than each system checking its own conditions. One place decides whether the tower defense is live, and everything else reads it.

ClockWork

A 2D action-combat game whose enemy learns how you fight: a persistent memory layer reads your habits across every encounter and reshapes the AI to answer them.

An underground encounter. Health, stamina and mana sit top-left and the ability wheel bottom-right, all driven by signals from the player data singleton rather than polled each frame. The enemy's bar is the vessel's combat pool: deplete it and the role either recasts into a new vessel or dies for good.
An underground encounter. Health, stamina and mana sit top-left and the ability wheel bottom-right, all driven by signals from the player data singleton rather than polled each frame. The enemy's bar is the vessel's combat pool: deplete it and the role either recasts into a new vessel or dies for good.
Ledge grab, with contextual prompts bound to the live input map so they follow rebinds and controller layouts.
Ledge grab, with contextual prompts bound to the live input map so they follow rebinds and controller layouts.
Traversal between camera zones, which clamp and reframe as the player crosses them.
Traversal between camera zones, which clamp and reframe as the player crosses them.
Title screen.
Title screen.
Tools:
Godot 4.7, GDScript, Hierarchical state machines, Jolt physics, Phantom Camera, Forward+ renderer
Platforms:
PC
Role:
Gameplay Programmer for Fiverr (@grayster001) · 2026 · In Development

ClockWork is a 2D action platformer built around melee combat: a light and heavy attack chain, dash, shield block, parry, and a magic set, all driven through hierarchical state machines for both the player and the enemy.

What makes it more than an HP bar is what each hit records. Every hit also reads how the player is fighting, whether that is brutal, precise, or repetitive, and those readings accumulate in a place the enemy itself cannot reach.

That split is the core of the design. The enemy vessel holds the state of a single life: its composure, its combat pool, how intact its role is, how much the audience favours it. The vessel keeps no memory of its own. A separate persistent layer does that, tracking behavioural patterns across every encounter and pushing the accumulated weight back onto the vessel. A player who wins by mashing the same heavy attack finds the enemy getting faster and more erratic over time, because the game has been reading those habits rather than nudging a hidden difficulty slider.

Transition legality lives in one dictionary mapping each state to the states it may enter. The machine walks that map at startup and registers an event per transition, so states change by dispatching a named event rather than by reaching into each other. Restricting a move: say, blocking dash-out-of-parry, is an edit in one place.

Player input never lives inside individual states. Every input-to-transition check sits together in the state machine, and each state calls only the subset legal from it. Adding a new input-driven move means writing one helper and referencing it from the states that should honour it.

Attack hitboxes activate on specific animation frames rather than on a timer, connecting to the sprite's frame signal on entry and disconnecting on exit. On a landed hit the hitbox does double duty: it applies damage and files a behavioural reading of the attack that produced it.

The enemy's difficulty comes out of the simulation rather than from a setting. Volatility and poise from the vessel feed a function that outputs move speed, attack cooldown, aim jitter, heavy-attack probability, and how long stagger and recovery last. Sustained brutal or repetitive play raises volatility, and the fight visibly tightens in response.

NightShift at Candie's

A survival horror night-shift game where six animatronics each hunt the player by different rules: built as a fully data-driven system the client can retune without touching code.

The security monitor. The camera map on the right is the player's whole interface for tracking six animatronics across twelve rooms.
The security monitor. The camera map on the right is the player's whole interface for tracking six animatronics across twelve rooms.
The office. Clock, monitor, mask, and door controls all worked from this one seat.
The office. Clock, monitor, mask, and door controls all worked from this one seat.
Two animatronics on one camera in the dining area, each running its own routing logic.
Two animatronics on one camera in the dining area, each running its own routing logic.
Feeds stay readable in bright rooms as well as dark ones.
Feeds stay readable in bright rooms as well as dark ones.
Main menu, with night progress restored by the save system.
Main menu, with night progress restored by the save system.
Tools:
Godot 4.6, GDScript, Forward+ renderer, JSON-driven configuration, Signal-based architecture
Platforms:
PC
Role:
Gameplay Programmer for Fiverr (@z_angel) · 2026 · Client Delivered

A single-location survival horror game in the night-watchman tradition: the player sits in an office, works a bank of security cameras, and manages power, doors, and a mask while six animatronics converge on them. I handled the gameplay programming, every system between the main menu and the jumpscare.

The interesting constraint was that no two animatronics could behave the same way. Frednic tracks a gaze meter that drains when he's ignored and fills when he's watched, so both staring and looking away will kill you. Bronnie walks a fixed door route and forces the door at the end of it. Dave rolls one of three approach routes at the start of a run and is stopped by closed doors. Rena escalates through poses on one camera before committing to the window or the right door. The Ambassador occupies several cameras at once and punishes the player for dropping the monitor while she's on screen. Fredbear announces himself with a trumpet, then walks in on a timer the player has to beat with a gaze button.

Because the client needed to tune difficulty themselves, every one of those behaviours reads its numbers from a documented JSON file at startup. Roam intervals, meter drain rates, route probabilities, strike counts, gaze deadlines: all editable in a text file, with inline comments explaining what each value does. Retuning a night is a reload, not a code change.

Every animatronic reads its tuning from a single JSON file loaded at startup, with the meaning of each value documented inline next to it. The client can change how fast Bronnie walks, how hard Frednic's meter swings, or how many strikes the Ambassador allows, then reload the game to feel the difference. None of it requires opening a script.

The gaze meter along the top edge, shown while the animatronic it belongs to is on camera.
The gaze meter along the top edge, shown while the animatronic it belongs to is on camera.

Frednic runs on a gaze meter rather than a patrol route. Ignore him and it drains; watch him and it fills. Cross either end of the range and he comes for the office. The player cannot solve him by picking one camera and staying there, since staying safe means keeping a rhythm of watching and looking away rather than holding a single view.

The Ambassador breaks the one-animatronic-one-camera assumption the rest of the roster follows. She occupies up to three cameras at once and tracks strikes against the player for dropping the monitor while she is on screen. Early strikes brick the camera she is on; the last one ends the run.

Night progression is driven by two tables: which animatronics are active on a given night, and what AI level each one runs at. Raising a level subtracts from every interval that animatronic uses, so difficulty scales through the same numbers designers already tune. Custom night exposes those levels directly to the player.

Cross-system communication runs on Godot signals through a small set of autoload singletons: night state, game clock, door state, camera state, and saves. The mask, for instance, announces itself when raised, and only the animatronics it should fool are listening.

Credits

Programming
Bilal AhmadGameplay systems, animatronic AI, night progression, save system, and UI wiring: the work described on this page.
Environment model
DiscoHeadRestaurant layout and textures, from the “FNaC Semi-Accurate Map” asset.
Input prompt icons
Kenneykenney.nl, released under CC0.
Character models, audio, and fonts
Various community creatorsSupplied for the project; individual authors still to be confirmed.
Original series
Five Nights at Candy’s, created by Emil MackoThis is a non-commercial fan project. The characters and setting belong to their creators.

Small Fiverr projects

Shorter commissions and focused one off builds.

Race 3D Combat Layer

Enemy AI, destructibles and a survival HUD layered onto a commercial racing template: navmesh-driven pursuers with raycast line-of-sight that hunt the player's vehicle.

The survival HUD over the template's drift smoke and skidmarks. Health and stamina are mine; the driving model is the template's.
The survival HUD over the template's drift smoke and skidmarks. Health and stamina are mine; the driving model is the template's.
Health, stamina and speed against the session maximum.
Health, stamina and speed against the session maximum.
Line-of-sight debug draw. The green and red rays are the detection check that decides whether an enemy commits to a chase.
Line-of-sight debug draw. The green and red rays are the detection check that decides whether an enemy commits to a chase.
Tools:
Godot 4.5, GDScript, NavigationAgent3D, LimboAI, Forward+ renderer
Platforms:
PC, Mobile
Role:
Gameplay Programmer for Fiverr (@a_narar) · 2025 · Prototype

This one starts from a bought racing template rather than an empty project, so it is worth being precise about the split. The template supplied the vehicles, the circuit, the car controllers, drift smoke, skidmarks, lights and the car select menu. What I built on top is the part that turns a driving demo into something with stakes: enemies that hunt you, objects that break, and resources you can lose.

The enemies run a hierarchical state machine over a navigation agent, moving between roam, chase and search behaviours. Detection is a raycast line-of-sight check on an interval rather than a plain area overlap, with a deliberate delay before a state change commits: without that debounce, an enemy standing at the edge of cover flickers between chase and search every frame.

Detection is a raycast on an interval, not a trigger volume. An area overlap alone would let an enemy see through walls; the ray confirms the sightline before the state machine acts on it. A separate delay gates how quickly a state change commits, which is what stops an enemy at the edge of cover from flickering between chase and search on alternating frames.

Every behaviour value is exported: roam, chase and search speeds, idle ranges, roam radius, shot interval, damage figures for each attack type, bullet speed. The AI is tuned in the inspector rather than by editing the state scripts.

Credits

Programming
Bilal AhmadEnemy AI, detection, attacks, destructibles, and the survival HUD: the work described on this page.
Base project
RNB Race 3D TemplateVehicles, race circuit, player and AI car controllers, drift smoke, skidmarks, lights, engine sound, and the car select menu.
Destruction plugin
JummitMIT licensed. Mesh fracture support behind the destructible objects.
Voronoi fracture
Robert VaradanMIT licensed. VoronoiShatter.
Mobile input
Virtual Joystick addonOn-screen joystick used for touch controls.

Stealth Game

A short 3D stealth build: cross a patrolled compound, collect the key and both plane wings, and repair the plane without tripping a detector.

The compound. Objective counters for the key and both wings sit top-left.
The compound. Objective counters for the key and both wings sit top-left.
Detection volumes drawn in red. Entering one alerts every enemy in the scene at once, not just the tower that saw you.
Detection volumes drawn in red. Entering one alerts every enemy in the scene at once, not just the tower that saw you.
Staged hint prompts fire on timers as each objective stage is reached.
Staged hint prompts fire on timers as each objective stage is reached.
Tools:
Godot 4.6, GDScript, Forward+ renderer, Area-based detection
Platforms:
PC
Role:
Gameplay Programmer for Fiverr (@nordaaaa) · 2026 · Prototype

A compact stealth prototype. The player crosses a walled compound gathering a key and two plane wings, then returns them to a grounded plane to escape. Guard towers and patrolling ships sweep the ground with detection volumes; walking into one is the fail state.

The alert model is what makes it read as stealth rather than as an obstacle course. Detection is not per-guard: a tower that spots you broadcasts to every enemy in the scene at once, and all of them switch into a frenzy state together. Getting seen escalates the whole level, not just the corner you were caught in.

Detector volumes re-test the bodies already inside them every physics frame rather than trusting the entry event alone. A body that enters while not yet detectable (still hidden, or not yet in the tracked group) is caught the moment that changes, instead of slipping through because it was already overlapping.

Alert state is global by design. A guard tower that spots the player pushes every enemy in the scene into frenzy simultaneously, so a single mistake escalates the whole level rather than one patrol route.