How to add in-game ads to your HTML5 game with the naughty.games SDK
Load https://naughty.games/sdk/v1.js with a script tag in the head of your index.html, then call NgSdk.commercialBreak() at natural pauses and NgSdk.rewardedBreak() when a player chooses to watch an ad for a reward. Both return promises that always settle: a commercial break can finish at once, and a rewarded break resolves true only when the player watched the ad to the end. Ads run only in builds hosted on naughty.games; on localhost the SDK shows a test ad instead, and ready-made wrappers exist for Unity, Godot 4, Construct 3 and GameMaker.
Last checked
Using an AI coding agent? Copy this prompt
Paste it into Claude Code, Cursor, Copilot or any coding agent. It adds the SDK to your project and tests it.
Preview the prompt
Goal: integrate the naughty.games in-game ads SDK (lifecycle and ad breaks only) into this HTML5 (plain browser game) project.
Setup:
1. Add this tag inside <head> of index.html, before the game's own scripts. Use the plain tag exactly, not a copy of the file:
<script src="https://naughty.games/sdk/v1.js"></script>
2. Wire the SDK into the game code (see "API" and "Rules" below). No wrapper file is needed: the global is NgSdk.
API (global NgSdk, added by the script tag; the engine wrappers call these for you):
- NgSdk.init(): Promise of { env, locale }. Call it first.
- NgSdk.gameLoadingFinished(): the game finished loading.
- NgSdk.gameplayStart() / NgSdk.gameplayStop(): active play starts or stops (menu, pause screen, game over).
- NgSdk.commercialBreak(): Promise of nothing. Shows an ad if one is due.
- NgSdk.rewardedBreak(): Promise of true or false. true only when the player watched the ad to the end.
- NgSdk.on('pause' | 'resume' | 'mute' | 'unmute', fn): fired around every ad.
Rules:
- Every call settles, and the game must carry on once it does. A commercial break may finish at once (no ad, or too soon), so never wait for an ad to appear.
- Grant a reward only when rewardedBreak() resolves true. Not on the button tap, not when the promise settles.
- On pause and mute, stop the game loop and silence audio; restore both on resume and unmute. Keep the handlers quick, they can arrive back to back.
- Place commercial breaks at natural pauses (between levels, after game over), never mid-action or mid-dialogue, and not at startup.
- Put rewarded breaks behind a button the player chooses to tap, and say what the reward is first. Never block progress behind one: on false the player keeps playing.
Verify:
- Run the game from localhost, or add ?ngsdk=test to its address. A test ad covers the game with Complete and Skip buttons. Check both paths: Complete must grant the reward, Skip must not, and the game must continue after each.
- Zip the build with index.html at the top level and upload it. The Build tab in the naughty.games developer console shows "SDK detected" when an HTML file in the build loads https://naughty.games/sdk/v1.js.
Ask me before changing any gameplay. If anything is unclear, read the full guide at https://naughty.games/guides/in-game-ads-sdk before guessing.What you need
- A browser build of your game that you upload to naughty.games as a zip. The SDK talks to naughty.games only from a hosted build; an embed URL build or a copy on another site gets no ads.
- Access to your build's
index.html, or to the engine setting that writes it (a Unity WebGL template, Godot's Head Include, or the exported file for Construct 3 and GameMaker). - Two places in your game where an ad fits: a natural pause for a commercial break, and something worth a reward for a rewarded ad.
The SDK is small (under 10 KB), has no dependencies, and adds one global object, NgSdk. It covers your game's lifecycle and ad breaks, nothing else.
Load the SDK
Add this line inside <head> of your index.html, before your game's own scripts:
<script src="https://naughty.games/sdk/v1.js"></script>
Use the plain tag exactly like this, not a copy of the file: v1 gets fixes without you re-uploading, and the developer console looks for this tag to show "SDK detected" on your build. A breaking change would ship as a new file name, so v1 keeps working.
API reference
| Call | Returns | What it does |
|---|---|---|
NgSdk.init() |
Promise of { env, locale } |
Connects to the page. env is naughty on naughty.games, local in test mode and inert anywhere else. locale is the player's language, for example en. Settles within 1.5 seconds. |
NgSdk.gameLoadingFinished() |
nothing | Your game finished loading and can be played. |
NgSdk.gameplayStart() |
nothing | Active play started (or resumed after a menu). |
NgSdk.gameplayStop() |
nothing | Active play stopped: a menu, a pause screen, a game over. |
NgSdk.commercialBreak() |
Promise of nothing |
Shows an ad if one is due. Continue your game when it settles. |
NgSdk.rewardedBreak() |
Promise of true or false |
Shows a rewarded ad. true only when the player watched it to the end. |
NgSdk.on(event, fn) |
nothing | Calls fn on pause, resume, mute or unmute. |
You can call breaks and lifecycle calls before init() settles; they wait for it. Calling init() first is still the clearest order.
Pause and mute when asked
Before an ad, the page sends pause and mute. After it, resume and unmute. The page cannot silence your game's audio from outside, so your game has to do it:
NgSdk.on('pause', () => game.pause());
NgSdk.on('resume', () => game.resume());
NgSdk.on('mute', () => audio.mute());
NgSdk.on('unmute', () => audio.unmute());
These can arrive back to back when no ad was available, so keep the handlers quick.
Where to place breaks
- Commercial breaks go at natural pauses: between levels, after a game over, before the next chapter. Never in the middle of action or dialogue, and not at startup: naughty.games may already show an ad before your game starts.
- Rewarded ads are always the player's choice: a button such as "Watch an ad for 50 coins" or "Watch an ad to continue". Say what the reward is before they tap.
- Never block progress behind a rewarded ad. If it resolves
false, the player keeps playing without the bonus.
A plain HTML5 example
<!doctype html>
<html>
<head>
<script src="https://naughty.games/sdk/v1.js"></script>
</head>
<body>
<button id="bonus">Watch an ad for 50 coins</button>
<script>
NgSdk.on('pause', () => game.pause());
NgSdk.on('resume', () => game.resume());
NgSdk.on('mute', () => audio.mute());
NgSdk.on('unmute', () => audio.unmute());
NgSdk.init().then(() => {
NgSdk.gameLoadingFinished();
NgSdk.gameplayStart();
});
async function onLevelComplete() {
NgSdk.gameplayStop();
await NgSdk.commercialBreak(); // may finish at once
NgSdk.gameplayStart();
startNextLevel();
}
document.getElementById('bonus').addEventListener('click', async () => {
const granted = await NgSdk.rewardedBreak();
if (granted) {
addCoins(50);
} else {
showMessage('No ad right now. Try again later.');
}
});
</script>
</body>
</html>
How breaks behave
Every call settles, and your game must carry on once it does:
- No ad available:
commercialBreak()settles at once andrewardedBreak()resolvesfalse. - Too soon: commercial breaks are spaced out per player session. Right now that is at least 3 minutes between breaks and none in the first 60 seconds after your game starts. A call inside those limits settles at once. The limits may change, so do not build your game around the exact numbers.
- One at a time: while a rewarded ad plays, another
rewardedBreak()call does not start a second one. - Something went wrong: every call has a timeout, so a lost connection never leaves your game waiting forever.
Grant a reward only when rewardedBreak() resolves true. Not on the button tap, not when the promise settles, not when pause arrives.
Test it locally
On localhost, 127.0.0.1 or a file:// page, or with ?ngsdk=test added to the address, the SDK runs in test mode (env is local):
- A test ad covers the game with Complete and Skip buttons. Complete resolves a rewarded break
true; Skip resolves itfalse, so you can check both paths. - If you click neither, the test ad closes by itself after 5 seconds and counts as completed.
pause,mute,resumeandunmutefire around the test ad just like the real thing.- Lifecycle calls are logged to the browser console as
[naughty.games SDK mock] gameplayStartand so on.
Unity and Godot builds need a local web server anyway (see the Unity and Godot export guides), and both serve on localhost, so test mode turns on by itself. Real ads only play once the build is hosted on naughty.games.
Engine wrappers
Each wrapper is a small, commented file you add to your project. They all need the script tag above in your page, and they all finish breaks at once if the SDK is missing, so your game never waits.
Unity (WebGL)
Download NaughtyGamesSdk.jslib into Assets/Plugins/WebGL/ and NaughtyGamesSdk.cs anywhere under Assets/. Attach the NaughtyGamesSdk component to an empty GameObject in your first scene; it stays across scene loads.
For the script tag, copy Unity's Default template from your Editor install (Editor/Data/PlaybackEngines/WebGLSupport/BuildTools/WebGLTemplates/Default) to Assets/WebGLTemplates/NaughtyGames/, add the tag inside <head> of its index.html, and pick it under Player Settings > Web > Resolution and Presentation > WebGL Template.
NaughtyGamesSdk.Instance.GameLoadingFinished();
NaughtyGamesSdk.Instance.CommercialBreak(() => LoadNextLevel());
NaughtyGamesSdk.Instance.RewardedBreak(granted => { if (granted) AddCoins(50); });
The component sets Time.timeScale to 0 and mutes AudioListener during ads (both can be turned off in the Inspector). In the Editor there is no ad: breaks finish at once, and a tick box decides whether a rewarded break grants, so you can test your reward code.
Godot 4
Download naughty_games_sdk.gd and add it as an autoload named NaughtyGamesSdk (Project > Project Settings > Globals > Autoload). In Project > Export > Web, paste the script tag into HTML > Head Include. The wrapper uses JavaScriptBridge, so it works in GDScript projects.
NaughtyGamesSdk.game_loading_finished()
await NaughtyGamesSdk.commercial_break()
if await NaughtyGamesSdk.rewarded_break():
add_coins(50)
It pauses the scene tree and mutes the Master bus during ads, and emits paused, resumed, muted and unmuted signals if you want to do more.
Construct 3
Download naughty-games-sdk.js, add it to your project's Scripts folder and set its Purpose to Main script. Set Project Properties > Advanced > Use worker to No, and export with Minify script set to None or Simple. After each Web export, add the script tag inside <head> of the exported index.html.
From a Run script action in an event sheet, call ngCommercialBreak() or ngRewardedBreak(). The wrapper answers by calling event sheet functions: NgBreakDone after a commercial break, and NgRewardDone with 1 when the player watched the ad to the end or 0 when not. It sets the time scale to 0 during ads and calls NgMute and NgUnmute so you can silence audio.
GameMaker (HTML5)
Download naughty_games_sdk.js and scr_naughty_games_sdk.gml. Create an extension, add the JavaScript file for the HTML5 target and add the functions listed at the top of that file. Add the GML file as a script. After export, add the script tag inside <head> of index.html.
// Persistent controller object
// Create: ng_sdk_start();
// Step: ng_sdk_step();
ng_sdk_commercial_break(function() { room_goto_next(); });
ng_sdk_rewarded_break(function(_granted) { if (_granted) global.coins += 50; });
GameMaker cannot take a JavaScript callback on HTML5, so the extension holds each result until ng_sdk_step() collects it and runs your function. During an ad, global.ng_paused is true and the master gain is 0.
Check it in the developer console
After you upload a zip, the Build tab shows SDK detected when an HTML file in your build loads https://naughty.games/sdk/v1.js with a script tag, or SDK not detected when none does. Once players play your live game, it also shows the last day their plays reached the SDK, which catches a build that loads the script some other way. The owner's preview never counts toward that.
How revenue share works
The SDK plays ads on your game page, so they follow the same deal as every other ad there:
You receive 70% of the net revenue from ads served by our ad network partners on your game page, including video and display ads. Earnings are paid in USDC on the Polygon network on the 10th of each month once your balance reaches 50 USDC.
That applies to games in the revenue share program, which covers builds hosted on naughty.games. On the free listing you keep 100% of what your own links earn, and there is no share of ad revenue, from breaks or anything else. The full rules are in section 8 of the developer terms.
Do not click ads on your own game page, ask players to click them, or send bot or bought traffic to it. Ad networks remove this traffic, and we treat it as breaking these terms.
Builds may not include ad code from other networks. The SDK is the way to show ads inside your game.
Publish it
Once your game shows the test ad on localhost and carries on after both Complete and Skip, zip the build with index.html at the top level (up to 250 MB unpacked and 2,000 files) and upload it. Review usually takes about 2 working days. Head to /submit when you are ready.
Questions
- Do ads from the SDK count toward my revenue share?
- Yes, if your game is in the revenue share program. You receive 70% of the net revenue from ads served by our ad network partners on your game page, including video and display ads. Breaks you call through the SDK play on your game page, so an ad our ad network partners serve in a break counts the same as any other ad there.
- Why did commercialBreak() finish straight away?
- That is normal. A commercial break finishes at once when no ad is available, when the last one showed less than 3 minutes ago, or in the first 60 seconds after your game starts. These limits may change, so continue your game when the promise settles, whatever happened.
- Does the SDK work on itch.io or in an embed URL build?
- No ads run there. The SDK connects only on builds hosted on naughty.games (uploaded as a zip). Anywhere else every break finishes at once and rewardedBreak() resolves false, so the same build still plays fine on other sites.
- Can I use another ad network in my build instead?
- No. Builds may not include third-party ad code; the developer console flags it and such builds are rejected. The SDK is the way to show ads inside your game on naughty.games.
- How do I test ads without uploading?
- Run the game from localhost or 127.0.0.1, or add ?ngsdk=test to the address. The SDK then shows a test ad with Complete and Skip buttons. It closes by itself after 5 seconds and counts as completed if you click neither.
Sources
- naughty.games developer terms (revenue share program, section 8.2)
- MDN: Window.postMessage()
- Unity Manual: Interaction with browser scripting
- Unity Manual: Web templates
- Godot docs: JavaScriptBridge
- Godot docs: Exporting for the Web
- Construct 3 manual: IRuntime script interface
- GameMaker manual: Extensions
Free hosting, real players on phone and desktop, and a stats console.