Write in VS Code
.luau files on disk, strict mode, luau-lsp resolving requires from a generated sourcemap. Autocomplete that actually knows the DataModel.
I'm mrosadev. I build Roblox games the way games with real player counts are built — VS Code, Rojo, Git, module scripts, server authority. Not click-and-drag in Studio. Then I design the UI that goes on top of them, and it ships as a documented system, not a pile of frames.
Every figure below is pulled straight from the Roblox Games API, not
rounded up for a portfolio.
These are the presentation boards from my own files — real frames, composited over real gameplay. Nothing here is a mockup of a mockup. Click any board to open it full size.
Every frame below is the real export from my Figma file, at full resolution. Each project opens on its showcase board, then runs through the individual screens — arrow keys, the filmstrip, or hit play and let it run. Click any frame to open it full size.
The HOOKED! file carries 56 screens on a documented system: eight colour ramps bound to variables, a type scale, a 48-icon library and ~50 components. Below are the real ramps, unedited — click any swatch to copy it.
— every swatch is bound to a Figma variable, which means a palette change is one edit, not a hunt through 56 screens.
The ramp above is not re-typed by hand in Studio. It is exported once into a theme module that every controller reads, so the file and the game cannot drift apart.
-- ReplicatedStorage/Shared/UI/Theme.luau
return {
rust = {
[500] = Color3.fromHex("C74E2C"),
[600] = Color3.fromHex("A33A22"),
},
brass = {
[500] = Color3.fromHex("DDA82E"),
},
}
Studio is a viewport and a playtest button. It is not where the code lives. Everything below is version-controlled, linted and reviewable as a diff — which is the only reason a game stays workable past a few thousand lines.
.luau files on disk, strict mode, luau-lsp resolving requires from a generated sourcemap. Autocomplete that actually knows the DataModel.
One default.project.json maps filesystem to DataModel. $ignoreUnknownInstances set explicitly on every container, because the default is the one that deletes map work.
Terrain, meshes, VFX, Lighting and Figma-imported UI hierarchies stay authored in Studio and get committed as .rbxmx. Code never round-trips through it.
Rokit pins the toolchain so CI and every machine match. StyLua and Selene gate the commit. One concern per commit — a security fix never rides along with a folder move.
Rate limit per player, payload validation, reject and log. One place, every remote.
The client never owns a number that matters. It says "I want to open this case." The server decides what came out. Anything else is a free item generator.
Economic mutations are atomic. Currency out and item in happen in one transaction, or neither happens. A crash between the two is a support ticket you can't answer.
One module knows the schema. One adapter touches DataStoreService. Scattered writes are how save data quietly rots.
Two hundred doors get one component, not two hundred scripts. Tags and attributes, a single module. Copy-pasted handlers are the debt that compounds fastest.
Figma UI is the View. Logic lives in .luau controllers that bind to the imported hierarchy. Good UI doesn't get rebuilt in code for the fun of it.
Smallest change that fits. No unsolicited refactors, no framework, no service locator. Extend what exists before adding a second system for the same job.
Four files from the shape these projects actually take. The first one is the roll behind the case-opening demo further up this page.