Quick answer: melonJS games are JavaScript running in the browser on canvas or WebGL, so crashes are JavaScript errors that land in the console and disappear when the player closes the tab. Hook window error and unhandledrejection, capture the stack trace and the active game state, attach browser and renderer context, and group duplicates so you triage from a dashboard instead of forum posts.
melonJS is a lightweight HTML5 game engine that runs entirely in the browser, rendering through canvas or WebGL and driving your game with a state-and-entity model. Because it is plain JavaScript on the web, its crashes are JavaScript errors, and the web platform makes them almost invisible: an uncaught error prints to the developer console the player will never open, the game freezes or half-runs, and the player just closes the tab. You hear about it, if at all, as a vague forum complaint. This post covers how melonJS errors surface, why the browser hides them, and how to capture stack traces, renderer state, and context that make them fixable.
How melonJS errors surface
A melonJS game runs an internal loop that updates and draws the active game state and its entities each frame. When your code throws, whether reading a property of an undefined object, calling a method that does not exist, or mishandling a pooled entity that was already recycled, the error propagates to the browser as an uncaught exception. The loop may stop, the canvas freezes on the last frame, or the game limps along in a broken state depending on where the throw happened. The JavaScript engine logs it to the console with a stack trace and moves on.
Asynchronous failures add a second category. melonJS loads assets and may use promises for fetching data or audio, and a rejected promise that nothing catches becomes an unhandledrejection event rather than a synchronous error. These slip past a simple try and catch around your update logic. Together, synchronous throws and unhandled rejections cover the bulk of melonJS crashes, and both are caught at the window level rather than inside your game code if you want full coverage.
Why the browser hides crashes
The web is a hostile environment for learning about crashes. Players run dozens of browser and version combinations, on desktop and mobile, with extensions that inject scripts, ad blockers that break asset loads, and wildly different GPU and canvas implementations. A WebGL context can be lost when the device sleeps or another tab demands resources, producing errors that have nothing to do with your logic. None of this reaches you by default, because the console is closed and there is no crash dialog on the web.
Minification compounds the problem. Shipped melonJS games are usually bundled and minified, so the stack trace a player would see, if they looked, points at single-letter function names and one giant line. Without source maps, even a captured trace is hard to read. The combination of an invisible console, enormous platform variance, and minified code is why browser game crashes feel unknowable until you deliberately capture them with enough context to overcome each of those obstacles.
Hooking window errors and rejections
The two essential hooks are window.addEventListener for the error event and for the unhandledrejection event. The error handler gives you the message, the source file, line and column, and crucially the Error object with its stack property when available. The unhandledrejection handler gives you the rejection reason, which is often an Error with its own stack. Registering both early, before the game starts, ensures you catch synchronous throws from the game loop and async failures from asset and data loading alike.
Capture the full stack string, not just the message, and ship source maps for your minified bundle so frames resolve back to your real functions and lines. You can also wrap the melonJS update step in try and catch as an extra layer to attach gameplay context at the precise moment of failure. The pairing of global handlers for completeness and a targeted wrapper for context is what gives you both coverage and detail, so a captured melonJS crash reads like a real stack trace rather than an opaque console blip.
Setting it up with Bugnet
Bugnet is a strong fit for melonJS because it is a web-native product and your hooks are just JavaScript. In your window error and unhandledrejection handlers, hand Bugnet the error message, the stack trace, and a context object with the active game state name, the entity involved, and the renderer mode. The crash arrives as a readable trace, resolved through your source maps, alongside the browser, version, and canvas-or-WebGL fields. You can also drop an in-game report button onto a melonJS UI container so a player who notices a glitch can describe it while the current state is attached automatically.
On the dashboard, Bugnet folds identical errors into one issue with an occurrence count, so the same undefined property read across thousands of sessions is a single ranked item instead of a console message you never saw. Custom fields for browser, renderer, and game version let you filter at once: if a crash only hits Safari, only fires under WebGL, or correlates with WebGL context loss on mobile, the grouped view makes it obvious. That turns the browser's silence into a prioritized list you can actually clear.
Renderer and device context that matters
Beyond the stack trace, the fields that disambiguate melonJS crashes are the renderer and the device. Capture whether the game is using canvas or WebGL, the browser name and version, whether it is mobile or desktop, and ideally the GPU renderer string exposed through WebGL. Rendering crashes and context-loss errors cluster on particular mobile GPUs and browser versions, so without these fields a WebGL fault looks identical to a logic bug, and you waste time reading code that is fine.
Add gameplay context: the active game state, the level, and a short breadcrumb of recent player actions. melonJS structures games as a stack of states, so recording the current state name narrows a crash to a screen immediately. Memory pressure matters on mobile, where a tab can be killed for resources, surfacing as a vanished game rather than a clean error. With renderer, browser, and state attached to every report, a melonJS crash stops being a guessing game and becomes a filterable record you can act on.
Testing across the browser matrix
Browser games demand testing across the matrix, not just your favorite Chrome window. Add a debug command that throws a deliberate error and one that rejects a promise, then confirm both report with stack traces and the right context from Chrome, Firefox, Safari, and at least one mobile browser. Test a forced WebGL context loss and confirm it reports distinctly from a logic error, since players will trigger context loss simply by locking their phones. Verify your source maps resolve frames in the deployed, minified build, not just in development.
Bake this into every release. The web tempts you to ship after testing one browser because the build is instant, but Safari and mobile fail in ways desktop Chrome never shows. With both error hooks proven across the matrix, source maps deployed, and crashes grouped and tagged by browser and renderer, your post-launch picture is a single ranked list. You fix the error hurting the most players first instead of chasing whichever forum post happened to be loudest, and the browser's invisible console stops working against you.
melonJS crashes vanish into a console nobody opens. Hook the window events, ship source maps, tag the browser and renderer, and grouping turns silence into a ranked list.