Custom Games
Adding a game to the UI
The UI lives in ui/app.js. Each game is an entry in the GAMES table:
JavaScript
GAMES.example = {
title: 'My game', width: 24,
build(g, o) {
// g is the game context, o is the options sent from Lua
const btn = h('button', { text: 'Click me', on: { click: () => g.finish(true) } });
g.body.append(btn);
g.hint('Press ', kbd('E'), ' or click');
g.key(e => { if (e.key.toLowerCase() === 'e') g.finish(true); });
g.timer(o.time);
},
};Use the context helpers instead of raw timers and listeners. They clean up when the game ends.
| Helper | Use |
|---|---|
g.body |
The element you build the game in. |
g.key(fn) |
Keydown handler. Key repeat is ignored. Pass true as the second argument to receive repeats. |
g.on(target, type, fn) |
Event listener, removed at the end. |
g.after(ms, fn) and g.every(ms, fn) |
Timers that stop when the game ends. |
g.loop((dt, ts) => {}) |
Animation loop. dt is in seconds. |
g.timer(seconds, onExpire) |
Shows the time bar. Fails the game on expiry, unless you pass your own handler. |
g.stat(text) and g.hint(...nodes) |
Header status text and footer hint. |
g.finish(success, delay) |
Ends the game and shows the result. |
Adding the Lua export
Copy this into client/main.lua, and add a matching table in shared/config.lua:
Lua
exports('Example', function(a1, a2)
if gameOpen then return false end
local opts, cb
if IsCallable(a1) then opts, cb = {}, a1
elseif type(a1) == 'table' then opts, cb = a1, a2
else opts, cb = { time = a1 }, a2 end
local o = {}
o.time = Num(opts.time, Default('ExampleDefaults', 'time', 15), 0, 60, true)
return Launch('example', o, cb)
end)The action name passed to Launch is the lowercase key in GAMES.
Recipes
Lock a door behind a pick
Lua
local function TryLockpick(door)
if exports['sbminigames']:IsOpen() then return end
exports['sbminigames']:Lockpick({ pins = 4, picks = 3, time = 40 }, function(success)
if success then
TriggerServerEvent('myresource:unlockDoor', door)
else
print('The pick snapped.')
end
end)
endRun games one after another
Lua
CreateThread(function()
if not exports['sbminigames']:Keypad({ length = 5 }) then return end
if not exports['sbminigames']:Wires({ pairs = 4 }) then return end
if not exports['sbminigames']:Scrambler({ length = 6 }) then return end
print('Hacked')
end)Harder with every attempt
Lua
local attempt = 0
local function Hack()
attempt += 1
exports['sbminigames']:Circle({
difficulty = attempt > 2 and 'expert' or 'medium',
circles = math.min(attempt + 1, 6),
}, function(success)
if success then attempt = 0 end
end)
endKeep rewards on the server
A minigame result is decided on the player's machine, so a cheater can fake true. Never give a reward just because a client event says the player won. Check on the server that the player is near the target, has the required item, and hasn't triggered the reward recently. Keep the minigame as the player-facing part only.
Last updated 2026-11-08
