How to add a support chat to an HTML5 game
Web games get a lot of players and almost no way to hear from them. A comment on the portal page is public, slow, and usually says "it froze". A chat inside the game reaches you while the player is still there, and it can tell you which browser, which level and which error, without asking.
Published 31 October 2026 by the Inngame team. The first half applies whatever tool you use; the second half shows how it's done with Inngame, which we make.
What makes web games different
A support chat for a downloaded game and one for a browser game solve the same problem, but the browser adds its own constraints:
- You live in someone else's iframe. On itch.io, CrazyGames, Poki and most portals, your game runs in an iframe on the portal's page. The chat has to fit inside your game's frame, not open a popup or a new tab the player will lose.
- Storage can disappear. An embedded game is a third-party context. Browsers increasingly partition or clear storage there, so an anonymous player can come back as a stranger. If your game has accounts, attach the conversation to the account, not to the browser.
- The useful context is different. Instead of a device model and a scene name, you want the browser and its version, the viewport, the page URL (which portal, which build), and above all the console errors from just before the player wrote.
- Your CSS and the chat's CSS must not fight. Game pages often reset styles globally or draw everything on one canvas. A chat that inherits
* { margin: 0 }or your pixel font will look broken. - Phones are half your players. On a small screen the chat must go full screen, keep the text field above the keyboard, and resize photos before uploading them on mobile data.
- Nobody waits on a web page. If the reply comes in two hours, the player is gone. Telling them when you usually answer, and what time you're back, sets the right expectation.
Building it yourself
The front end is the easy part: a panel, a list and a text field. The work is behind it: a server that stores conversations per player, real-time delivery (WebSocket with a fallback when it drops), photo storage with moderation if your audience is young, an inbox for your team, and a way to delete a player's data when they ask. If you only need a contact form, a mailto: link is honest and costs nothing. If you want replies inside the game, the backend is where the time goes.
Adding it with Inngame, step by step
1. Create the app and get the key
Sign up, create an app in the inbox, and copy its public key from Settings → Apps → General. The key identifies the game; it is not a secret.
2. Add one script tag
Host inngame.js with your game, or load it from our site, and add it after your game's scripts:
<script src="inngame.js" data-app-key="app_…"></script>
That's a working chat: a round button in the corner, the interface in the browser's language (25 languages), and your team's replies translated for the player.
3. Open it from your own menu
A floating button can sit on top of your controls. Hide it and open the chat from your pause or settings screen instead, with a badge for unread replies:
Inngame.init({ appKey: "app_…", launcher: false, appVersion: "1.4.0" });
helpButton.onclick = () => Inngame.open();
Inngame.on("unread", (n) => helpBadge.textContent = n || "");
Inngame.on("open", () => game.pause());
Inngame.on("close", () => game.resume());
4. Send the context your team needs
Every message already carries the page URL, browser, viewport, language, time zone and the last 50 console errors and warnings. Add your game's state with a function; it runs when the player sends a message, so it's always current:
Inngame.init({
appKey: "app_…",
context: () => ({ level: state.level, coins: state.coins, seed: state.seed }),
});
5. Know who the player is
If your game has accounts, identify the player after login so their conversation follows them to another browser. Pass a token signed by your server to make it a verified identity; without it, the id is only a label.
await Inngame.identify({ userId: account.id, name: account.nickname, token: account.inngameJwt });
6. Let your team give things back
When a bug costs a player their progress, your team can send an item card from the inbox. The player taps Claim, and your callback runs only if the server confirms this claim won, so a double tap or a second tab never grants it twice:
Inngame.init({
appKey: "app_…",
onItemGranted: (item) => inventory.add(item.id, item.quantity),
});
Events for automations (Inngame.track("level_failed", { level: 3 })), store review cards and satisfaction ratings are in the web SDK docs.
Before you ship: a test checklist
- Upload a build to the portal (itch.io lets you keep the page in draft) and open the chat inside the real iframe, on desktop and on a phone.
- On the phone, write a message with the keyboard open and send a photo from the gallery.
- Reply from the inbox and check the reply appears without reloading, and the badge counts it when the chat is closed.
- Throw an error in the console, send a message, and find it in the conversation's context.
- Write in another language and check both sides read their own.
- Reload the page: the conversation should still be there. Then try a private window to see what a new player gets.
- Read your portal's rules on external links and data collection before you add review or link cards.
Questions
Does it work inside the itch.io or CrazyGames iframe?
Yes. The chat is drawn inside your game's page, so it stays within the iframe the portal gives you. Use launcher: false and open it from your own menu if a floating button would cover your game's controls.
Will the player keep their conversation between visits?
Usually. The SDK keeps a device token in localStorage. Some browsers partition or clear storage for games embedded on another site, and then a returning player starts a new anonymous conversation. Calling identify with your own account id keeps the history attached to their account.
How big is the script?
About 64 KB, 22 KB gzipped, with no dependencies. It loads after your game and does nothing until it gets a session.
Can I use it with Phaser, PixiJS, Godot or Construct exports?
Yes. It doesn't touch your canvas or game loop: it adds one element with its own Shadow DOM, so your CSS and its CSS can't clash.
What happens if the player is offline?
The chat shows an offline banner and the message waits. It is kept in the browser, even if the page is closed, and sent when the connection comes back or on the next visit, never twice.
Related: Support players in every language · Discord or in-game support? · Support chat in a Unity game · A Helpshift alternative for indie game studios · Player data requests · Web SDK docs · Inngame for HTML5 games · Inngame for web apps