DEV Community

Cover image for Unity in React.js: From 20 Seconds to 1.2 Seconds loading time
Nikola Perišić
Nikola Perišić

Posted on

4

Unity in React.js: From 20 Seconds to 1.2 Seconds loading time

I optimized the loading time of Unity game from over 20 seconds to just 1.2 seconds, achieving a ~94% improvement.

Want to know how?

🔍 Have you heard of service workers?

In this post, we'll dive into what service workers are 🤔, how they work ⚙️, and how I used them to improve the performance of my web application.


What is a Service Worker? 🤖
A service worker is a background script that runs separately from the web page, and can:

  • Cache assets 🗂️
  • Intercept network requests 🌐
  • Deliver offline functionality 🚫

Key Characteristics of Service Workers:

  1. Runs in the background: Service workers are not tied to the page's lifecycle, meaning they can continue running even if the page is closed
  2. Event-driven: Service workers listen to specific events, such as network requests (fetch event)
  3. No DOM access: Unlike other web workers, service workers don't have access to the DOM directly. They operate in the background, managing resources and network traffic

Here's a simple example of Service Worker script:

ServiceWorkerExample.js🔍

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/service-worker.js').then(function(registration) {
      console.log('Service Worker registered with scope:', registration.scope);
    }).catch(function(error) {
      console.error('Service Worker registration failed:', error);
    });
  });
}
Enter fullscreen mode Exit fullscreen mode

In this snippet, the service worker is registered when the page loads. If the registration is successful, it logs the service worker's scope; otherwise, it logs an error.


My Challenge: Integrating a Unity WebGL Game into a web page

I was integrating a Unity WebGL game into a React.js website. The game consisted of large ".wasm" and ".data" files alongside JavaScript files that were crucial for the game to load. However, the first time the page loaded, it took over 20 seconds! 🚨 This was primarily due to the size of the ".wasm" and ".data" files.


The Solution: Caching with Service Workers

I created a "ServiceWorker.js" file, which caches all the necessary game assets to reduce the loading time. Here's what the file contains:

const cacheName = "$Project_name";
const contentToCache = [
    "Build/Game.loader.js",
    "Build/Game.framework.js",
    "Build/Game.data",
    "Build/Game.wasm",
    "TemplateData/game_style.css"
];

self.addEventListener('install', function (e) {
    console.log('[Service Worker] Install');

    e.waitUntil((async function () {
        try {
            const cache = await caches.open(cacheName);
            console.log('[Service Worker] Caching all: app shell and content');
            for (const resource of contentToCache) {
                try {
                    await cache.add(resource);
                } catch (error) {
                    console.error(`[Service Worker] Failed to cache: ${resource}`, error);
                }
            }
        } catch (error) {
            console.error('[Service Worker] Failed to open cache', error);
        }
    })());
});

self.addEventListener('fetch', function (e) {
     e.respondWith((async function () {
        try {
            const response = await caches.match(e.request);
            console.log(`[Service Worker] Fetching resource: ${e.request.url}`);
            if (response) return response;

            const fetchResponse = await fetch(e.request);
            const cache = await caches.open(cacheName);
            console.log(`[Service Worker] Caching new resource: ${e.request.url}`);
            await cache.put(e.request, fetchResponse.clone());
            return fetchResponse;
        } catch (error) {
            console.error(`[Service Worker] Error fetching resource: ${e.request.url}`, error);
            throw error;
        }
    })());
});
Enter fullscreen mode Exit fullscreen mode

What does this code do?

This service worker script performs the following actions:

  1. Caches essential files needed for the game to run, including ".wasm", ".data", and JS files during the install phase.
  2. Intercepts fetch requests to serve cached resources if available. If the resource isn't in the cache, it fetches it from the network, caches it, and returns the response.

The Result: Significant Performance Boost

By caching these assets, the difference was night and day - what once took over 20 seconds now loaded in just 1.2 seconds🚀. The next time users visit the page, the assets are already stored locally, drastically speeding up the experience.

⚠️ Be cautious! The Caching Problem

While this approach works for improving performance, it introduced another issue: outdated cached files.

Since we're actively developing the Unity game and releasing new versions, the cached files needed to be cleared every time a new version was deployed.

To solve this, I created a pipeline script that automatically unregisters the old service worker and clears the cache whenever we publish a new game build. This ensures that users always load the latest assets without being stuck with old, cached versions.


Conclusion
Implementing service workers in your web application can dramatically improve performance, especially when dealing with large assets like in my Unity WebGL game. However, it's important to handle caching carefully to avoid serving outdated files.

Have you had similar experiences optimizing load times? Did you use service workers or other techniques? Share your thoughts and insights in the comments below! 💬

Hope you learned something new today! ✌️

LinkedIn

Sentry blog image

How to reduce TTFB

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

In this article, we’ll see how we can identify what makes our TTFB high so we can fix it.

Read more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay