Summary
Rendering a moderately large tilemap (≥ ~2000 visible/batched tiles in one layer) on Flutter web with the skwasm renderer crashes the engine with RuntimeError: memory access out of bounds.
Root cause is upstream in Flutter, not in flame_tiled itself, but reporting here so anyone hitting the crash finds the workaround quickly.
Steps to reproduce
flutter run -d chrome --wasm with _flutter.loader.load({config: {renderer: "skwasm"}}) in web/flutter_bootstrap.js.
- Load any
TiledComponent whose visible tile-layer batch exceeds ~2000 tiles. A 64×64 ground layer triggers it.
Trace
RuntimeError: memory access out of bounds
at $malloc (skwasm.wasm)
at $paint_create (skwasm.wasm)
at $SkwasmPaint.toRawPaint (paint.dart:16)
at $SkwasmCanvas.drawAtlas (canvas.dart, inside withStackScope)
at $SpriteBatch.render (flame sprite_batch.dart:520)
at $FlameTileLayer.render (tile_layer.dart:147)
at $RenderableTiledMap.render (renderable_tile_map.dart:487)
at $TiledComponent.render (tiled_component.dart:83)
FlameTileLayer.render calls SpriteBatch.render which calls Canvas.drawAtlas once per layer with one entry per tile. Skwasm's drawAtlas writes the per-entry arrays into the wasm stack (_emscripten_stack_alloc, default 64 KiB), and ~2000+ entries overflow the stack into the heap; the next malloc traps.
Upstream
Once that fix lands, no flame_tiled change is needed.
Workaround until upstream fix is released
final tiled = await TiledComponent.load(
'wedding_pilgrimage.tmx',
Vector2.all(32),
useAtlas: false, // bypasses SpriteBatch.drawAtlas → uses drawImageRect per tile
);
useAtlas: false switches FlameTileLayer to a per-tile Canvas.drawImageRect path instead of SpriteBatch.drawAtlas. It boots fine, but expect ~10× lower FPS in debug and a measurable drop in release because the GPU batching is gone (in our case ~100 FPS skwasm + useAtlas: false vs ~120 FPS canvaskit + useAtlas: true). Acceptable as a stop-gap; not as a permanent solution.
The other workaround is to switch the web bootstrap to renderer: "canvaskit", which doesn't share this code path.
Suggested README/docs note
A short note in the flame_tiled README under "Web / known issues" pointing at flutter/flutter#185995 and the useAtlas: false escape hatch would save future users a lot of time — the crash signature points at paint_create rather than drawAtlas, so it's easy to misdiagnose. Happy to PR the docs change if useful.
Versions
flame: ^1.37.0
flame_tiled: ^3.1.1
- Flutter
3.44.0-0.3.pre, Dart 3.12.0-327.4.beta, engine 285ae5207
- macOS 15 / arm64, Chrome stable
Summary
Rendering a moderately large tilemap (≥ ~2000 visible/batched tiles in one layer) on Flutter web with the
skwasmrenderer crashes the engine withRuntimeError: memory access out of bounds.Root cause is upstream in Flutter, not in
flame_tileditself, but reporting here so anyone hitting the crash finds the workaround quickly.Steps to reproduce
flutter run -d chrome --wasmwith_flutter.loader.load({config: {renderer: "skwasm"}})inweb/flutter_bootstrap.js.TiledComponentwhose visible tile-layer batch exceeds ~2000 tiles. A 64×64 ground layer triggers it.Trace
FlameTileLayer.rendercallsSpriteBatch.renderwhich callsCanvas.drawAtlasonce per layer with one entry per tile. Skwasm'sdrawAtlaswrites the per-entry arrays into the wasm stack (_emscripten_stack_alloc, default 64 KiB), and ~2000+ entries overflow the stack into the heap; the nextmalloctraps.Upstream
Once that fix lands, no
flame_tiledchange is needed.Workaround until upstream fix is released
useAtlas: falseswitchesFlameTileLayerto a per-tileCanvas.drawImageRectpath instead ofSpriteBatch.drawAtlas. It boots fine, but expect ~10× lower FPS in debug and a measurable drop in release because the GPU batching is gone (in our case ~100 FPS skwasm +useAtlas: falsevs ~120 FPS canvaskit +useAtlas: true). Acceptable as a stop-gap; not as a permanent solution.The other workaround is to switch the web bootstrap to
renderer: "canvaskit", which doesn't share this code path.Suggested README/docs note
A short note in the
flame_tiledREADME under "Web / known issues" pointing at flutter/flutter#185995 and theuseAtlas: falseescape hatch would save future users a lot of time — the crash signature points atpaint_createrather thandrawAtlas, so it's easy to misdiagnose. Happy to PR the docs change if useful.Versions
flame: ^1.37.0flame_tiled: ^3.1.13.44.0-0.3.pre, Dart3.12.0-327.4.beta, engine285ae5207