Work

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

Portfolio

Client projects and larger builds.

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.