DEV Community

xbill for Google Developer Experts

Posted on • Originally published at Medium on

Midsommer Madness with WASM and Rust

This article covers debugging and deploying a Rust backed WASM module with a Firebase hosted web app celebrating the Swedish Midsommar traditions.

How did we get Here?

What started as a submission for the dev.to June Solstace Jam:

June Solstice Game Jam - DEV Challenge - DEV Community

Blossomed into a whole series exploring new approaches to building out interactive apps across various platforms:

midsommer Series' Articles

Earlier articles covered Web, Flutter, Firebase, and IOS/Android deployment.

So what is new about this installment?

This installment brings a WASM build with Rust to Midsommer Madness via Antigravity CLI. The Midsommer Madness web app is deployed to Firebase Web Hosting with Rust and WASM — and the Maypole was saved!

What I Built

When it comes to Summar Solstace — the place to be is Sweden. It is one of the highlights of the calendar.

This project aimed to recreate some of the mystique around the event- just in time for some fresh surestromming!

Now you can get it with Firebase enhancements! And WASM! And Rust!

So Why Am I reading this?

So what is different about this lab compared to all the others out there?

This is one of the first deep dives into deploying a Rust based WASM deployment ported from an existing working web application.

Is it comprehensive? Not really. The goal was to explore, extend, and exercise the powerful combination of a Rust backed WASM application.

Rust Setup

Instructions to install Rust are available here:

Getting started

For a Linux like environment the command looks like this:

curl — proto ‘=https’ — tlsv1.2 -sSf https://sh.rustup.rs | sh
Enter fullscreen mode Exit fullscreen mode

WASM

WebAssembly (often abbreviated as Wasm ) is a low-level binary instruction format that allows developers to run code written in languages like C, C++, Rust, and Go directly in modern web browsers at near-native speeds. [1, 2, 3]

How it Works

Unlike traditional web code that requires step-by-step interpretation, Wasm compiles code into a compact binary format ahead of time. [1]

  • Compilation Target: You don’t write Wasm directly. Instead, you write high-level code in your preferred language and use a compiler to translate it into a .wasm file. [1, 2, 3, 4, 5]
  • The Virtual Machine: This binary is run inside a lightweight, stack-based virtual machine built into the browser. [1, 2]
  • Teamwork with JavaScript: Wasm is designed to complement JavaScript, not replace it. JavaScript is still used to structure the web page, while Wasm handles heavy computations in the background. [1, 2, 3]

Just show me the Code!!!

GitHub Repo is here:

GitHub - xbill9/midsommer-wasm

Firebase Tools

So how did we use Firebase to enhance the project?

Github and Skills!

The GitHub repository has been successfully linked to your Firebase project midsommer-madness .

### What was configured:
  1. GitHub Secrets: A service account ( github-action-1273534909 ) with Firebase Hosting admin permissions was
  created, and its credentials were uploaded to your GitHub repository secrets as                                 
  FIREBASE_SERVICE_ACCOUNT_MIDSOMMER_MADNESS .
  2. GitHub Actions Workflows: Two workflow files were generated and pushed to your GitHub repository:
      • firebase-hosting-pull-request.yml: Deploys a preview channel of the game whenever a PR is created against
the master branch.
      • firebase-hosting-merge.yml: Automatically deploys the latest version to the live Firebase Hosting site
whenever a PR  
      is merged (or a commit is pushed) to the master branch.
  3. Agent Skills: Installed Firebase Agent Skills in the repository to make it easier for agents to help you with
  Firebase configurations in the future.
> /skills
  ⎿ Exited /skills command
Enter fullscreen mode Exit fullscreen mode

OK it Got Weird. How Did Rust get involved?

Ferris is sneaky crab.

> how is Rust used in this project?

96.4k in | 501 out | 420 thinking
● Read(/home/xbill/midsommer-wasm/game-wasm/src/lib.rs) (ctrl+o to expand)

  In this project, Rust acts as the high-performance core engine of the game, compiled to WebAssembly (WASM). It manages
  physics calculations, collision systems, game entities, and particle logic, delegating rendering and media management to
  the JavaScript/browser layer.

  Here are the specific ways Rust is utilized in the codebase:

  ### 1. High-Performance Physics & Vector Math
  Rather than running heavy game physics and coordinate tracking in the main JavaScript thread, the game uses Rust for 60Hz
  fixed-timestep updates:

  • Collision Detection: Implements precise Axis-Aligned Bounding Box (AABB) collision checks ( check_aabb_collision ) and
  circle-based proximity checks ( check_circle_collision ).
  • Vector Mechanics: Handles directional speeds, distance calculations, and fast vector normalization directly in compiled
  machine code.
  ### 2. Zero-Allocation Particle System
  Under game-wasm/src/lib.rs , a custom particle emitter system handles up to 1000 simultaneous particles ( Particle 
  struct):

  • State Updates: Tracks coordinates, velocity, damping (friction/drag), life expectancy, and color indexes.
  • Performance Optimization: Utilizes flat, contiguous memory allocations ( static mut PARTICLES: [Particle; 1000] ) to
  process particle logic without memory allocations during runtime, keeping CPU caching friendly and ensuring smooth
  gameplay.

  ### 3. Centralized Entity State Logic

  The internal state of the player and active characters is maintained in Rust-native memory:

  • The Player Struct: Tracks attributes like health, coordinates, score, active status effects (speed boosts, shields), and
  weapon parameters—including uniquely Swedish themed ammo varieties ( ammo_surstromming and ammo_flatpack ).
  • The Enemy Struct: Houses state definitions for subclasses of enemies (e.g., Shoppers, Drunkards, ABBAbots).

  ### 4. Seamless JavaScript Bridging (WASM Interface)

  To bridge the memory boundaries between JavaScript and WebAssembly, Rust uses an extern "C" layout:

  • No-Mangle Primitive APIs: Employs #[no_mangle] pub extern "C" to export functions like get_particle_x(idx) or
  spawn_particle(...) using primitive data types ( f32 , i32 ) for absolute compatibility.
  • Inline Packaging: When compiling via the Makefile ( make build-wasm ), the compiled output in assets/game_physics.
  wasm is base64-inlined into assets/wasm_binary.js . This allows the game rendering file ( assets/game.js ) to load the
  game engine instantly inline without needing separate network requests. Use-Interaction-free.

Enter fullscreen mode Exit fullscreen mode

WASM, Rust, And Javascript? What’s Next? Flying Cars?

The high level design allows the performance sensitive calculations to be done within the memory safe Rust layer:

> Do WASM and Rust work well together?
  Yes, Rust and WebAssembly (WASM) are widely considered to be one of the strongest pairings in modern software engineering.
  They were designed with overlapping goals—speed, safety, and efficiency—and have developed highly mature ecosystems
  together.

  Here is why they work exceptionally well together:
  ### 1. No Garbage Collector (Zero-Cost Abstractions)
  Unlike languages like Go, C#, or Java, Rust does not require a garbage collector or a heavy runtime to manage memory.
  • Tiny Binary Footprints: Since WebAssembly payloads must be downloaded over the network to run in a browser, keeping
  compiled file sizes small is crucial.
  • Predictable Performance: Rust uses compile-time memory management (borrow checker), meaning there are no garbage
  collection pauses during gameplay or high-frequency calculations.

  ### 2. Memory Safety in a Linear Sandbox
  WebAssembly operates on a flat, linear memory space. In languages like C or C++, a buffer overflow can overwrite arbitrary
  locations in this memory, leading to major security exploits or hard-to-debug crashes.

  • Rust’s compile-time guarantees ensure that memory access is safe, bounds-checked, and free of data races before the code
  ever compiles to WASM.

  ### 3. Industry-Leading Tooling

  The Rust community has built the most advanced tools for WebAssembly development:

  • wasm32-unknown-unknown : A native compiler target built directly into the Rust compiler ( rustc ), allowing you to
  compile Rust to WASM out of the box with zero dependencies.
  • wasm-pack : A one-stop CLI tool that compiles Rust code to WASM, optimizes it (using wasm-opt ), and packages it with
  auto-generated JavaScript/TypeScript bindings.
  • wasm-bindgen & web-sys : Libraries that enable high-performance, seamless sharing of complex structures (objects,
  strings, arrays) between Rust and JavaScript, and provide direct, type-safe access to browser APIs (like DOM, WebGL, and
  Web Audio).

  ### 4. Code Reuse Across Ecosystems

  You can write high-performance computation blocks once in Rust, compile them to WebAssembly, and run them anywhere:

  • In the browser (as done in this project's canvas UI).
  • On mobile devices (wrapped in Flutter webviews or React Native).
  • On servers and edge computing platforms (Cloudflare Workers, Fastly).

Enter fullscreen mode Exit fullscreen mode

My Lingonberries are ripening- get to work!

How do you deploy this?

> make help

8.2k in | 112 out | 45 thinking
 Bash(make help) (ctrl+o to expand)

    ========================================================================
    🇸🇪 Midsommer Madness Flutter Build & Development Controls 🇸🇪
      ========================================================================
      Available commands:
        make dev - Start the local web server for browser play (from assets/)
        make build-wasm - Compile the Rust WebAssembly physics and particle engine
        make test - Run the Flutter unit/widget test suite
        make build-apk - Compile the Flutter App and build Debug APK
        make build-ios - Compile the Flutter App and build iOS app (no codesign)
        make install-apk - Install the compiled debug APK on a connected device/emulator
        make clean - Clean Flutter build outputs and temporary caches
        make logcat - Monitor application logs using Flutter logger
        make deploy - Deploy the game to Firebase Hosting (manual bypass)
        make firebase-logs - Fetch the latest cloud logs from Google Cloud / Firebase
        make firebase-emulators - Start local Firebase Emulator Suite (Firestore & Hosting)
        make deploy-preview - Deploy a temporary preview channel to Firebase Hosting
        make firebase-status - Check current Firebase project configurations
        make deploy-rules - Deploy security rules for Cloud Firestore
        Note: Pushing/merging to 'master' on GitHub automatically triggers deployment.
      ========================================================================



Enter fullscreen mode Exit fullscreen mode

WASM + Rust Build:

> make build-wasm

● Bash(make build-wasm) (ctrl+o to expand)

    Compiling Rust WebAssembly module...
    cd game-wasm && cargo build --target wasm32-unknown-unknown --release
        Finished `release` profile [optimized] target(s) in 0.01s
    WebAssembly compiled and base64 helper assets/wasm_binary.js generated successfully!

Enter fullscreen mode Exit fullscreen mode

Deploying to Firebase Hosting:

> make deploy

13.2k in | 612 out | 495 thinking
● Bash(make deploy) (ctrl+o to expand)

  The manual deployment to Firebase Hosting completed successfully!

  ### 🚀 Deployment Details

  • Target Target: make deploy ( npx -y firebase-tools deploy --only hosting )
  • Project: midsommer-madness 
  • Status: Successful (9 files compiled and uploaded from assets/ directory)
  • Production URLs:
      • 🌐 Live App Link https://midsommer-madness.web.app
      • 🛠️ Firebase Project Console https://console.firebase.google.com/project/midsommer-madness/overview

  The deployed web assets include the optimized Rust WebAssembly physics module ( assets/game_physics.wasm ), inline Base64
  helpers ( assets/wasm_binary.js ), and the procedural Web Audio synthesis managers.

  Let me know if you would like me to inspect deployment rules, test out native compilation, or explore further optimize
  gameplay parameters, or explore other features!
Enter fullscreen mode Exit fullscreen mode

Rust Code Patterns

Is Rust Overkill for a simple web game? Probably.

The interesting pattern is the matching of Rust + WASM along with some Javascript glue code.

What does Antigravity CLI have to say about this?

> what does the combo of Rust and WASM bring to this project?
  The combination of Rust and WebAssembly (WASM) brings several distinct architectural and execution-level advantages
  specifically tailored to the requirements of this hybrid Flutter/Web game:
  ──────
  ### 1. Jitter-Free 60Hz Physics (Eliminating Garbage Collection)
  • The Challenge: Standard JavaScript games frequently suffer from micro-stuttering or "jitter" because of the
  browser's Garbage Collector (GC) reclaiming memory from expired particles, projectiles, and dead enemies.
  • The Rust + WASM Solution: In lib.rs, all game entities are kept in fixed-size, pre-allocated static
  arrays ( PARTICLES , ENEMIES , PROJECTILES ). Because Rust handles memory directly without a garbage collector
  and WASM operates on a flat, linear memory space, there are zero heap allocations or deallocations during gameplay.
  This guarantees that the physics tick maintains a stable 16.6ms frame budget.
  ### 2. High-Density Simulation Capability
  • At any moment, the game can track:
      • 1,000 active particles (running position updates, damping velocity, and linear array compaction).
      • 100 enemies (processing complex AI routines, state machines, and distance checks).
      • 200 projectiles (testing bounds, collision overlapping, and ticking Area-of-Effect surströmming gas).
  • WASM runs at near-native speed. This execution density allows the physics loop to complete in microseconds,
  leaving the majority of the frame time for the browser's main thread to handle CSS transitions, canvas rendering,
  and procedural audio synthesis.
  ### 3. Inline Zero-Fetch Loading (Base64 JS Module)
  • The Challenge: Traditional WASM deployment requires fetching a .wasm file over the network, which can trigger
  CORS issues, MIME-type mismatches, or file-not-found errors in local mobile webviews (like Flutter's WebViewWidget
  ).
  • The Rust + WASM Solution: The compiled WASM bytecode is serialized directly into a Base64 string inside 
  assets/wasm_binary.js . Rust compiles down to a highly optimized, compact binary (using opt-level = "s" as noted
  in Cargo.toml ). This allows the entire physics module to be loaded inline instantly from memory with zero
  additional HTTP network requests.

  ### 4. Mathematical Determinism Across Platforms

  • JavaScript floats can sometimes behave slightly differently depending on the browser engine's JIT implementation
  (V8 on Android, JavaScriptCore on iOS).
  • Because WebAssembly compiles Rust's explicit math logic directly into standard WASM bytecode instructions,
  computations like distance , check_circle_collision , and trigonometric AI angles ( atan2 , cos , sin ) yield
  identical results on any hardware or operating system.

  ### 5. Clear Separation of Concerns (MVC Boundary)

  The combination enforces a strict architectural boundary:

  • Rust/WASM (The Model/Simulation): Calculates the absolute state, velocities, damage ticks, AI states, and
  coordinate limits.
  • JavaScript/Canvas (The View/Presentation): Polls the WASM memory buffer, handles touch/keyboard inputs, renders
  sprites onto the HTML5 Canvas, and triggers procedural synthesizers.
  ──────
Enter fullscreen mode Exit fullscreen mode

Run it on the Web!

The current version is now available at:

Midsommer Madness - Swedish Retro Arcade

https://midsommer-madness.web.app
Enter fullscreen mode Exit fullscreen mode

Watch the Leader Board

Looks like Rolfe has been skilling up!

Level Design

The key levels include:

  1. IKEA Warehouse: Battle crowded, flatpack-carrying shoppers who throw box projectiles at you.
  2. Systembolaget: The state-owned liquor shop crowded with drunk Swedes stumbling and lobbing green beer bottles.
  3. Lördagsgodis: Sugar rush Saturday! Dodge hyperactive, strung-out Swedish kids throwing sweet candy projectiles.
  4. The Swedish Pub: Sing along with Frank Zappa fans singing “Bobby Brown” (shouting and firing glowing letters B, O, B, B, Y).
  5. Volvo Highway: A survival lane-crossing level where…

This is Fun! How do I add my own levels?

Antigravity CLI has full visibility into the game:

> add a godis level where strung out kids throw lordagsgodis ▸ Thought for 2s, 525 tokens Considering Level Placement I will search for level ID checks referencing level 9 in game.js to prepare for adding a 10th level. ● Search(Search for lvl.id === 9 checks) (ctrl+o to expand) I will view lines 495 to 585 in game.js to inspect the current level configurations. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) ▸ Thought for 2s, 1.8k tokens Sequencing the Levels I will view the setLevelAudioMode method in game.js to prepare for adding Level 3 "Lördagsgodis". ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will update the setLevelAudioMode method in game.js to map the scales and tempos for the 10-level layout. ● Edit(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view the current level checks in tickSequencer inside game.js to prepare the next set of replacements. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will shift the level conditionals in the music sequencer tickSequencer method to accommodate the 10-level progression. ● Edit(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view lines 920 to 935 of game.js to locate the exact Maypole check in initLevel . ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view lines 1060 to 1070 of game.js. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view lines 1290 to 1300 of game.js. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view lines 1360 to 1375 of game.js. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view lines 1370 to 1385 of game.js. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view lines 1460 to 1475 of game.js. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will update the level ID checks for the final Maypole level from 9 to 10 and register the candy-throwing kids target name in the HUD. ● Edit(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view the start of the levels array in game.js to prepare the level insertion edit. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view lines 545 to 585 of game.js. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) ▸ Thought for 3s, 1.2k tokens Refining The Level Design I will update the levels array in game.js to insert Lördagsgodis as Level 3, shift subsequent level IDs, and re-calculate scores. ● Edit(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view lines 950 to 985 of game.js to locate the spawnEnemy switch statement. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view lines 985 to 1010 of game.js. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will add the "kid" case to the spawnEnemy switch block. ● Edit(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) ▸ Thought for 3s, 2.1k tokens Considering Projectile Design I will search for class ZappaFan in game.js to locate a suitable place to insert CandyKid and Lordagsgodis . ● Search(Search for ZappaFan in game.js) (ctrl+o to expand) I will view lines 2120 to 2145 in game.js. ● Read(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will insert the CandyKid and Lordagsgodis class definitions in game.js right after the Drunkard class. ● Edit(/home/xbill/midsommer-madness/game.js) (ctrl+o to expand) I will view lines 275 to 325 of index.html. ● Read(/home/xbill/midsommer-madness/index.html) (ctrl+o to expand) I will update index.html to add Level 3 Lördagsgodis to the sidebar levels tracker and update levels 4 through 10. ● Edit(/home/xbill/midsommer-madness/index.html) (ctrl+o to expand) I will run a syntax check on game.js using node to ensure the new classes and level configurations are syntax-error free. ● Bash(node -c game.js) (ctrl+o to expand) I will view lines 20 to 45 of game_overview.md to inspect the levels sequence list. ● Read(/home/xbill/.gemini/antigravity-cli/brain/a...2ba-4622-b252-003672e1b73f/game_overview.md) (ctrl+o to expand) I will update game_overview.md to describe the 10-level layout including Lördagsgodis. ● Edit(/home/xbill/.gemini/antigravity-cli/brain/a...2ba-4622-b252-003672e1b73f/game_overview.md) (ctrl+o to expand) I have successfully added the Lördagsgodis level to Midsommer Madness, featuring hyperactive, sugar-rushed children throwing Saturday candy at Sven!
Enter fullscreen mode Exit fullscreen mode

Summary of Changes

  1. Level Setup ( game.js ): * Inserted Lördagsgodis as game.js in the levels array, shifting following levels up to Level 10 (ABBA Disco). * Updated all final-level check logic throughout game.js from Level 9 to Level 10.
  2. Added CandyKid & Lordagsgodis Classes ( game.js ): * Defined game.js and game.js right after the Drunkard class. * Behavior: * CandyKid has high speed (1.8), low health (35), and throws sweets rapidly. * Kids shiver/vibrate rapidly on screen to depict their extreme sugar rush. * Lordagsgodis are fast-moving candy projectiles (round jelly drops, square sweet gel blocks, and candy star crosses) that rotate in flight and come in a variety of bright colors.
  3. Music & Audio: * Modified game.js to include a happy, hyperactive 150 BPM C Major chiptune track for Level 3 (Lördagsgodis). * Shifted level conditions in game.js to map Levels 8, 9, and 10 to their respective custom chiptune hooks.
  4. UI & Documentation: * renumerated the sidebar tracker list in index.html to show the 10-level layout. * Updated the level progression descriptions in game_overview.md.

We have a Winner!

Midsommer is saved!

Phew! That was a lot!

This installment covered extending the Midsommer madness app with Rust and WASM to play the web based versions deployed to Firebase Hosting. Future enhancements under consideration include experimenting with WASM across mobile platforms.


Top comments (0)