Is there anyway I can cache models in the user's browser cache?

If I wanted to speed up the user’s load times on their next time loading my three.js game, could I possibly save some gltf models to their browser cache?

This is what browsers can do by default, though the server tells if, what and how to cache. Using a different approach like local storage might be another option, but this rather might not what people really like, since default caching works with timestamps so it can be release after a while like visitors who only play once.

1 Like

It’s actually pretty hard in a lot of systems to add cache headers to unusual file types. For example, I’m using cloudflare as a CDN and it’s just not possible to cache .glb or .fbx files. In spite of that, CF brings my best case load time from ~6 seconds to ~2 seconds, so it’s still worth it. I’m loading textures separately to models though to get as much cached as possible.

Even in systems where it is possible to cache arbitrary file types (e.g direct control over nginx/apache config) it still takes quite a bit of knowhow to add the headers. No server that I know of will cache models by default.

1 Like

Yeah you should at least have the ability to configure caching, you can define it for any kind of file extension then. But „abusing“ other storage types is very likely not what users whant. For example using localStorage, in anycase there will be a prompt about it and it can scare away users, especially those rather using it only once.

I think it’s worth using something else than CF being similar fast but being able to control the header, caching all files is faster and saves traffic, being than faster again in comparsion. But it depends where your focus is, like expecting the users to return often or just being fast as possible on the first visit.

What do you suggest? Bearing in mind that CF is already faster (including on repeat views) than other free or cheap solutions I’ve tried, even without caching the glTF models. It’s considerably faster in my testing than serving static assets directly from nginx with either heroku or digitalocean, for example (at least without spending big bucks for a higher tier hosting package).

CF is extremely simple to use, and extremely free, both of which are important considerations for me. I don’t have time (or desire) to learn how to use AWS as a CDN, which is the other obvious alternative that I’m aware of, and I suspect they may not cache glTF either, anyway.

:grinning:

If your resources are cached i don’t think CF could be faster, since they are already locally available on the device. A game for example with thousands and more persistent players it’ll definetly pay off and prevent it to get slower, idk about CF but a free plan is never unlimited and wouldn’t scale.

Once you have a app/game expecting visitors to return you only benefit from caching, even if the server isn’t the fastest, it’s only a 1 time thing then, depending on the update rate/cache lifetime of the assets. Plus you don’t have multiple requests like loading texture separately.

If you depend on a free service there might be just not that option, doesn’t cloud flare has a cheap option where you get to configure more?

I actually use a relatively cheap hoster, but the asset loader of my engine works with bundles, such as a bunch of assets being requested, being served and cached in one stream. You can do some things like optimizing compression and all, but at the end i wouldn’t expect to get really far with a free service for a big project, but im surprised CF even offers that for free.

CF brings my best case load time from ~6 seconds to ~2 seconds

But this also depends on the connection of the users, anyone living in the neverland or mobile would really appericate to not wait very long on each visit, asides of losing data volume.

Edit: Actually, have you tried to disguise your files as images by using a jpg extension? :grinning:

1 Like

$20 a month.

Ha, that’s an interesting idea. I might try it.

Edit …hmm, looks like there may be a way to get CF to respect the cache headers from my origin server…I’ll test that out tomorrow.

You get a vm for that price already :scream_cat:

In case you try that, i think the gltf loader looks for the extension being glb or gltf, so it either is enough to name it model.glb.jpg or you would need to patch that in the loader.

Like @Fyrestar said, you can set http headers for browsers to cache any file (see Cache-Control headers).

If you want Cloudflare to do the caching between your server and them, you can use Page Rules to cache any file extension. Just set a URL with a page rule of “Cache Everything” and Cloudflare will cache all files under that URL on their end.

See https://support.cloudflare.com/hc/en-us/articles/115000150272-How-do-I-use-Cache-Everything-with-Cloudflare-

Edit: I tested it with GLB and FBX, gives a cf-cache-status: HIT

In the end, I told it to just skip adding cache headers completely and configured everything in Nginx.
Works very well - looks like it’s shaved about 0.4 seconds off repeat views (down to 1.1 seconds now, best case, testing with fast computer and broadband).

@Fyrestar thanks for the push to look into this more deeply :grin:

There’s still gzip to consider - CF doesn’t compress .glb either and Webpagetest.org tells me that compressed .glb will be about 50% smaller. I’m a bit surprised by that since I though .glb was already reasonably compressed.

But one of the main reasons for using Cloudflare is so that my $5 a month VPS doesn’t get overwhelmed and I think doing compression on the VPS will cause more of a CPU hit. Could be wrong… but that’s more than enough server admin for one day!

I don’t think gltf has any compression without adding a corresponding extension. My own binary format results in similar size without compression being a mix of array buffers and json in the buffer itself, with pako it shrinks to less than 1/3.

Would in memory caching work for you? After the glTF is loaded, threejs does a lot of processing before it can be used. You could cache the processed Object3D and just provide a clone each time you need a new instance

I’m caching glb files using cache API

fetch(url)
            .then((response) => {
              if (response.body) {
                cache.put(request, response.clone()),
                response.arrayBuffer()
                    .then((data) => {
                      const glbBlob = new Blob([data], {type: 'model/gltf-binary'});
                      resolve(window.URL.createObjectURL(glbBlob));
                    });
              } else {
                reject(Error('Unable to Download Model'));
              }
            });

This looks great. However I cannot work out how to get GLTFLoader to load glb from the cache!
Would you share an example?

Thanks

nathamanath
This looks great. However I cannot work out how to get GLTFLoader to load glb from the cache!

in service worker

self.addEventListener("fetch", (event) => {
  if (event.request.method !== "GET") return;
  event.respondWith(
    (async () => {
      const url = event.request.url;
      const isGlb = url.endsWith(".glb");
      if (isGlb) {
        // Try to get the response from a cache.
        const cache = await caches.open(CACHE_NAME);
        const cachedResponse = await cache.match(event.request);

        if (cachedResponse) {
          // If we found a match in the cache, return it, but also
          // update the entry in the cache in the background.
          event.waitUntil(cache.add(event.request));
          return cachedResponse;
        }
      }

      // If we didn't find a match in the cache, use the network.
      const res = await fetch(event.request).catch(() =>
        caches.match(event.request).then((res) => res)
      );
      if (isGlb) {
        const copyCache = res.clone();
        caches.open(CACHE_NAME).then((cache) => {
          cache.put(event.request, copyCache);
        });
      }
      return res;
    })()
  );
});

There is also the option to convert your website to a PWA (Progressive Web App) which not only can maximize cache on all devices and OSs, it can even work offline, like an app.

Where I can find service Worker ?

You can save the models in IndexDB, which does not require PWA.

I am using a PWA and would like to do the following :

  • when I am online : download some GLB files to the cache
  • go offline
  • call my downloaded GLB and view those 3D assets

Could you please provide the best way to cache the GLB files (indexDB, cache, etc.) ?

Would you provide some example of such websites or code samples ?

Thank you for your input