I'm Miky, a Roblox scripter building clean, reliable systems.
3+ years of experience, 3 paid commissions delivered.
I focus on individual systems, not full games. Shops, NPCs, save systems, UIs, anything that size and self-contained.
I write organized, modular code. Everything is split into proper modules, structured with configs, and named clearly so anything is easy to find and change later.
I work feature by feature, sometimes a small bundle of features that need each other (shop + inventory + save, combat + UI). Whole games and oversized asks are out of scope.
Video demos on my YouTube channel
A fully functional NPC shop. The NPC patrols its shop area with pathfinding and interacts with players through a smooth camera transition and dialogue system. Includes buying, selling, inventory, currency, and DataStore-saved player data.
Watch demo
A wave-based survival sword fight where the enemies' avatars are your actual Roblox friends. Waves get progressively harder, every 3rd wave spawns a scaled-up mega boss, and your best wave is tracked on a leaderboard.
Watch demo
A sandbox with 10 unique interactable props, each with their own physics and effects. All prop logic is server-side with a shared ragdoll system tying everything together.
Watch demo--!strict
local Debris = game:GetService("Debris")
local Hitbox = {}
Hitbox.__index = Hitbox
export type Hitbox = typeof(setmetatable({} :: {
Size: Vector3,
Params: OverlapParams,
Debug: boolean,
}, Hitbox))
function Hitbox.new(size: Vector3): Hitbox
local self = setmetatable({}, Hitbox)
self.Size = size
self.Params = OverlapParams.new()
self.Params.FilterType = Enum.RaycastFilterType.Exclude
self.Params.FilterDescendantsInstances = {}
self.Debug = false
return self
end
function Hitbox:SetIgnoreList(list: {Instance})
-- have to set it, modifying the existing list does nothing
self.Params.FilterDescendantsInstances = list
end
function Hitbox:Scan(cf: CFrame): {Model}
if self.Debug then
self:_DrawDebug(cf)
end
local parts = workspace:GetPartBoundsInBox(cf, self.Size, self.Params)
local hits, seen = {}, {}
for _, part in parts do
local rig = part:FindFirstAncestorOfClass("Model")
if rig and not seen[rig] and rig:FindFirstChildOfClass("Humanoid") then
seen[rig] = true
table.insert(hits, rig)
end
end
return hits
end
function Hitbox:_DrawDebug(cf: CFrame)
local p = Instance.new("Part")
p.Size = self.Size
p.CFrame = cf
p.Anchored = true
p.CanCollide = false
p.CanQuery = false
p.Transparency = 0.7
p.Color = Color3.fromRGB(255, 80, 80)
p.Parent = workspace
Debris:AddItem(p, 0.15)
end
return Hitbox
--!strict
local DataStoreService = game:GetService("DataStoreService")
local store = DataStoreService:GetDataStore("PlayerData_v2")
local DEFAULT_PROFILE = {
Coins = 0,
Level = 1,
Wins = 0,
}
export type Profile = typeof(DEFAULT_PROFILE)
local PlayerData = {}
local function deepCopy(t: T): T
local out = {}
for k, v in t :: any do
out[k] = if type(v) == "table" then deepCopy(v) else v
end
return out :: any
end
function PlayerData.Load(userId: number): Profile
local key = "u_" .. userId
local ok, data = pcall(store.GetAsync, store, key)
-- if load fails return defaults but dont save them
-- saving defaults would overwrite real data later
if not ok then
warn(string.format("[PlayerData] load failed for %d: %s", userId, tostring(data)))
return deepCopy(DEFAULT_PROFILE)
end
return data or deepCopy(DEFAULT_PROFILE)
end
function PlayerData.Save(userId: number, profile: Profile): boolean
local key = "u_" .. userId
for attempt = 1, 3 do
local ok, err = pcall(store.SetAsync, store, key, profile)
if ok then
return true
end
warn(string.format("[PlayerData] save attempt %d failed: %s", attempt, tostring(err)))
task.wait(2 ^ attempt)
end
warn(string.format("[PlayerData] gave up saving for %d", userId))
return false
end
return PlayerData
--!strict
local Cooldown = {}
Cooldown.__index = Cooldown
export type Cooldown = typeof(setmetatable({} :: {
Duration: number,
_stamps: {[any]: number},
}, Cooldown))
function Cooldown.new(duration: number): Cooldown
return setmetatable({
Duration = duration,
_stamps = {},
}, Cooldown)
end
function Cooldown:TryUse(key: any): boolean
local last = self._stamps[key]
if last and (os.clock() - last) < self.Duration then
return false
end
self._stamps[key] = os.clock()
return true
end
function Cooldown:Remaining(key: any): number
local last = self._stamps[key]
if not last then
return 0
end
return math.max(0, self.Duration - (os.clock() - last))
end
function Cooldown:Reset(key: any?)
if key == nil then
table.clear(self._stamps)
else
self._stamps[key] = nil
end
end
return Cooldown
One standalone system or script. Combat, shop, NPC, data handler, or custom tool.
Multiple systems that work together. Shop + inventory + data saving, combat + UI, etc.
Pay per task by default. Each commission gets broken into small pieces and you pay for each one as it's delivered. If you'd rather do a simple 50/50 (upfront and on delivery), that works too. No rev-share, no "pay when the game ships." Gift cards and crypto aren't accepted; ask if you have something else in mind.
Quick and simple. No extra fees on your end.
Bank-to-bank transfer. Free with most US banks. Send a message for the handle or QR.
You cover the 30% Roblox tax, and the total must match the listed USD value at the current DevEx rate. Pay via gamepass or group funds.
As soon as a task is paid for, that code is fully yours. You can modify it, ship it anywhere, and I don't keep a copy.
You can walk away anytime. Per-task means you only pay for what's been delivered; with 50/50, the upfront half is refundable until I've started.
EST based. I reply within hours on Discord, slower on email.
Send me a message and tell me what you're building. Include the systems you need, your timeline, and a budget if you have one.