This Defold native extension provides access to the Poki SDK from Lua and supplies Poki-specific HTML5 loading, build, and bundle integration. Poki is an online playground where players and game developers come together to play and create. The games on Poki are created by a diverse global game developer community. Got a game? Submit it via developers.poki.com.

Defold 1.13.0 and newer versions use wasm-web only. Defold 1.13.0 removed js-web; legacy asm.js builds are not supported.
Before you dive in and start integrating the Poki SDK in your game it is recommended that you learn about some of the best practices when building for the web.
To use Poki SDK in your Defold project, add a version of the Poki SDK extension to your game.project dependencies:
https://github.com/defold/extension-poki-sdk/archive/refs/tags/<version>.zip
Replace <version> with the version you want to use, or copy the ZIP URL from the list of available Releases.

Select Project->Fetch Libraries once you have added the version to game.project to download the version and make it available in your project.

The extension adds two commands to the Defold editor:
wasm-web bundle and opens it in the Poki Inspector.
Keep the Defold editor open while testing: it serves the local HTML5 build to the browser.
The Poki Inspector helps you verify the integration before uploading the game:
measure() activity in the Event Log, together with detected errors and warnings;See the Poki Inspector guide for the complete testing workflow.

Use Scaling Tests to try different resolutions, aspect ratios, and mobile-device presets, including rotating supported devices to check portrait and landscape behavior—see the Defold GUI layouts manual for building adaptive interfaces.

wasm-web bundle, packages it as poki.zip, and opens the Poki upload flow.
Use the poki_sdk.gameplay_start() event to describe when users are playing your game (e.g. on first user interaction and unpause).
Use the poki_sdk.gameplay_stop() event to describe when users aren’t playing your game (e.g. level finish, game over, pause, quit to menu).
-- first level loads, player clicks anywhere
poki_sdk.gameplay_start()
-- player is playing
-- player loses round
poki_sdk.gameplay_stop()
-- game over screen pops up
Commercial breaks are used to display video ads and should be triggered on natural breaks in your game. Throughout the rest of your game, we recommend you implement the poki_sdk.commercial_break() before every poki_sdk.gameplay_start(), i.e. whenever the user has shown an intent to continue playing.
The Lua callback represents Poki’s commercialBreak(startCallback).then(...) flow: it receives COMMERCIAL_BREAK_START if Poki invokes the start callback, then COMMERCIAL_BREAK_SUCCESS when the promise resolves. A break opportunity does not necessarily display an ad.
-- gameplay stops
poki_sdk.commercial_break(function(self, status)
if status == poki_sdk.COMMERCIAL_BREAK_START then
print("Commercial break started, pause game")
elseif status == poki_sdk.COMMERCIAL_BREAK_SUCCESS then
print("Commercial break finished or did not happen")
end
end)
Not every single poki_sdk.commercial_break() will trigger an ad. Poki’s system will determine when a user is ready for another ad, so feel free to signal as many commercial break opportunities as possible.
Rewarded breaks allow for a user to choose to watch a rewarded video ad in exchange for a certain benefit in the game (e.g. more coins, etc.). When using poki_sdk.rewarded_break(), please make it clear to the player beforehand that they’re about to watch an ad.
The Lua callback maps Poki’s rewardedBreak({ size, onStart }).then(success => ...) flow to statuses. It receives REWARDED_BREAK_START if Poki invokes onStart, followed by REWARDED_BREAK_SUCCESS when the promise resolves to true or REWARDED_BREAK_ERROR when it resolves to false.
-- gameplay stops
poki_sdk.rewarded_break(function(self, status)
if status == poki_sdk.REWARDED_BREAK_ERROR then
print("Rewarded break resolved without a reward")
elseif status == poki_sdk.REWARDED_BREAK_START then
print("Rewarded break started")
elseif status == poki_sdk.REWARDED_BREAK_SUCCESS then
print("Rewarded break success")
end
end)
You can also request a reward size of "small", "medium", or "large". Omitting it defaults to "small":
poki_sdk.rewarded_break("medium", function(self, status)
if status == poki_sdk.REWARDED_BREAK_SUCCESS then
print("Grant the medium reward")
end
end)
Learn more about rewarded ads in the Poki monetization guide.
Calling poki_sdk.rewarded_break() affects the timing of poki_sdk.commercial_break() - When a user interacts with a rewarded break, our system’s ad timer is reset to ensure the user does not immediately see another ad.
Make sure that the game is paused and keyboard inputs are disabled during commercial_break and rewarded_break, so that the game doesn’t interfere with the ad:
-- gameplay stops
poki_sdk.rewarded_break(function(self, status)
if status == poki_sdk.REWARDED_BREAK_START then
print("Rewarded break start, pause game and disable input")
elseif status == poki_sdk.REWARDED_BREAK_ERROR or status == poki_sdk.REWARDED_BREAK_SUCCESS then
print("Rewarded break finished or did not happen, unpause game and enable input")
end
end)
Beginning with Poki Extension 3.3.0, sound will be muted automatically when ADS are shown.
Use poki_sdk.measure(category, what, action) to record meaningful checkpoints in a game. Parameters can be any strings. The Defold binding defaults omitted what and action to empty strings for compatibility, but this fallback is not the recommended event shape. Use custom strings for meaningful game events. Choose short, stable values that remain comparable across game versions:
category is the broad group, such as "level", "button", "rewarded", or "cosmetic".what identifies the specific level, placement, button, item, or system.action describes what happened. Poki gives special reporting meaning to start, complete, fail, visible, and interact; custom action values are also accepted.Commercial and rewarded ad playback and outcomes are tracked automatically through poki_sdk.commercial_break() and poki_sdk.rewarded_break(). Do not send measure() events for ad impressions, playback, completion, failure, or reward grant. Rewarded visible and interact events describe only the in-game offer UI.
See Poki’s Game Events documentation for more patterns and reporting guidance.
Some good practices for events:
Measure users’ progress to get statistics on how players are doing in your game.
Use the same category and what value for an attempt. Send start, followed by either complete or fail - never both for the same attempt:
-- Tutorial attempt
poki_sdk.measure("level", "tutorial", "start")
poki_sdk.measure("level", "tutorial", "complete") -- or "fail"
-- Level attempt
poki_sdk.measure("level", "1", "start")
poki_sdk.measure("level", "1", "complete") -- or "fail"
Measure statistics for rewarded ad offers—when these are visible to players and when players decide to use them.
Measure the in-game offer that leads to a rewarded video. Send visible when the offer becomes visible and interact when the player chooses it:
-- The treasure-chest offer is shown.
poki_sdk.measure("rewarded", "treasure-chest", "visible")
-- The player chooses the offer, immediately before requesting the ad.
poki_sdk.measure("rewarded", "treasure-chest", "interact")
poki_sdk.rewarded_break(function(self, status)
if status == poki_sdk.REWARDED_BREAK_SUCCESS then
-- Grant the treasure-chest reward.
end
end)
The placement name should describe the offer, not the ad result. Use the same category and placement for both events so exposure and engagement can be compared.
Measure other key gameplay or UI elements that might help you shape your game to better suit players or help them navigate to desired places.
The same visible and interact pair can be applied to shops, buttons, upgrades, power-ups, and other UI:
-- The shop button becomes visible in the menu.
poki_sdk.measure("button", "shop", "visible")
-- The player opens the shop.
poki_sdk.measure("button", "shop", "interact")
-- The player sees and selects a shop item.
poki_sdk.measure("shop-item", "extra-coins", "visible")
poki_sdk.measure("shop-item", "extra-coins", "interact")
Measure when players choose cosmetic upgrades or interact with meta-game features:
-- Count milestones and item-specific cosmetic unlocks.
poki_sdk.measure("cosmetic", "items", "unlock-2")
poki_sdk.measure("cosmetic", "hat", "unlocked")
Detect when players change the difficulty level if your game supports it, so that you know whether the available levels suit most players.
-- Measure difficulty changes
poki_sdk.measure("difficulty", "hard", "selected")
Congrats, you’ve successfully implemented the Poki SDK! Now upload your game to the Poki Inspector and test it there. When you’re happy with the implementation, send Poki a review request and they’ll play the game. Feel free to contact Poki via Discord or developersupport@poki.com if you’re stuck.
The Lua module is available only in HTML5 builds. When your project is also meant to be run on another platform, safeguard Poki extension calls with a small wrapper:
if html5 then
poki_sdk.gameplay_start()
end
Do not collect Lua errors manually using sys.set_error_handler(). The SDK collects Lua errors and the engine’s errors and warnings automatically.
poki_sdk.is_ad_blocked() remains available for compatibility but is deprecated and always returns false. Do not use it for game logic.
The extension includes poki-sdk/api/poki-sdk.script_api, which Defold uses for editor autocomplete and API documentation.
For VS Code users running Lua Language Server, poki-sdk/api/lls-annotations.lua provides equivalent Lua annotations. Add that file to the Lua workspace library only if the editor does not discover dependency annotations automatically.
You can create a shareable url with the following function:
local params = {
id = "myid",
type = "mytype",
score = 28
-- ... any other param
}
poki_sdk.shareable_url(params, function(self, url)
print(url)
-- if run on e.g. https://poki.com/en/g/my-awesome-game it will return:
-- https://poki.com/en/g/my-awesome-game?gdid=myid&gdtype=mytype&score=28
end)
-- read further to see how to fetch these params easily from within your game
As you might have noticed in the previous topic, the poki_sdk.shareable_url() creates a url with parameters that are prefixed with gd. We have created a simple helper function that will easily allow you to read the params.
poki_sdk.get_url_param("<param name>")
-- example
local id = poki_sdk.get_url_param("id")
-- this will return either the gdid param set on poki.com or the id param on the current url
The extension returns the string supplied by PokiSDK.getURLParam(key). If that JavaScript call returns null or undefined, the Defold bridge returns nil; Poki’s documentation does not otherwise specify a missing-parameter result.
Use poki_sdk.get_device_info() to get the current player’s device info. It returns a table that currently only has a category field, which is always mobile, tablet, or desktop. This is what the Poki platform itself uses to determine what to show.
local device_info = poki_sdk.get_device_info()
local category = device_info.category
On mobile, you can reposition the Poki Pill slightly to better fit your game UI using move_pill(topPercent, topPx).
topPercent is a number between 0 and 50 and sets the pill’s vertical position as a percentage from the top of the game area.topPx is an additional pixel offset applied on top of topPercent (positive moves it down, negative moves it up).You can’t move the pill lower than 50% of the game area (the game bar at the bottom is not included in this area).
The default position is move_pill(0, 24).
Poki Pill size
46px × 62px on screens narrower than 1211px.92px × 64px on screens 1211px wide or wider.-- Move the pill 100 pixels above the center of the game.
poki_sdk.move_pill(50, -100)
Most games should call poki_sdk.get_user() after load and treat user == nil with error == nil as “not signed in yet”.
poki_sdk.get_user(function(self, user, error)
if error then
return
end
if user then
print("Welcome", user.username)
else
print("User not logged in")
end
end)
Use poki_sdk.login() only in response to a user action that requires an account. A successful login can refresh the page and reload the game, so your game should call poki_sdk.get_user() again after load.
poki_sdk.get_token() is mainly for games that verify Poki users on their own backend. It returns token == nil when no user is logged in and the token is short-lived.
Never print or display an account token in a production game. The example reports only whether a token was received and its length.
The extension exposes poki_sdk.open_external_link(url) for user-initiated external navigation:
poki_sdk.open_external_link("https://developers.poki.com/")
Refer to the example project to see a complete example of how the integration works.
The source code is available on GitHub
The public Lua API corresponds to the original JavaScript API:
poki_sdk.gameplay_start() -- in JS it's PokiSDK.gameplayStart()
poki_sdk.gameplay_stop() -- in JS it's PokiSDK.gameplayStop()
poki_sdk.commercial_break(function(self, status) end) -- in JS it's PokiSDK.commercialBreak()
poki_sdk.rewarded_break(function(self, status) end) -- in JS it's PokiSDK.rewardedBreak()
poki_sdk.rewarded_break(size, function(self, status) end) -- uses `size`: "small", "medium", or "large"
poki_sdk.set_debug(value) -- in JS it's PokiSDK.setDebug(value)
poki_sdk.capture_error(error_string) -- in JS it's PokiSDK.captureError(error_string)
poki_sdk.shareable_url(params, callback) -- in JS it's PokiSDK.shareableURL({}).then(url => {})
local value = poki_sdk.get_url_param(key) -- in JS it's PokiSDK.getURLParam('id')
poki_sdk.get_device_info() -- in JS it's PokiSDK.getDeviceInfo()
poki_sdk.measure(category, what, action) -- in JS it's PokiSDK.measure(category, what, action)
poki_sdk.move_pill(topPercent, topPx) -- in JS it's PokiSDK.movePill(topPercent, topPx)
poki_sdk.get_user(callback) -- in JS it's PokiSDK.getUser().then(user => {})
poki_sdk.get_token(callback) -- in JS it's PokiSDK.getToken().then(token => {})
poki_sdk.login(callback) -- in JS it's PokiSDK.login().then(() => {})
poki_sdk.open_external_link(url) -- in JS it's PokiSDK.openExternalLink(url)