Optimize for bandwidth/storage space

The Mapillary web app has gained dangerously too much bloat lately. Sure, it has become a complex app over the last years and most of the data is cached once it has been loaded but asking 50.05 MB just to get things rolling is imho a bit much. Please keep in mind that more and more (if not most) people access the internet on mobile devices over metered connections, not over unlimited stable high speed wired connections. I do not know to what extent Mapillary collects such stats but I am pretty sure that especially Facebook should have some quite exact stats in this regard. Anyway, I can only urge you to optimize for bandwidth by setting yourself an ambitious hard target, like no more than 10 MB (which sounds realistic for the Mapillary web app). Without a specific hard target one is very quickly tempted to say “nah, it is good enough”.

At first glance, the largest size contributor seems to be the latest addition of NeRFs. For example, note that the page loads many quite heavy NeRF PNGs. In this specific case there are a few very easy and simple things you can do:

  • load heavy images only when needed
  • always use JPEG for natural content images
  • crank up the JPEG compression ratio for thumbnails to 0.75, or maybe even 0.5 (usually it is around 0.9)
  • if you have to use PNGs then consider using 8 bit palette mode
  • set yourself an ambitious hard target and check it on every update

Anyway, you have a lot of smart people on board and they will surely know what to do. :wink: Just please keep an :eye: on bandwidth for all those people who pay for every megabyte directly out of their :purse: pocket.

2 Likes

Thanks for your feedback! It does seem the thumbnails for the latest NeRFs we added are not compressed enough, I’ll update them ASAP.

1 Like

OK, this reduced the size by ~20MB and it will be visible as soon as our CDN picks up the updated files.
NeRF can also be turned off with a toggle in the “Map data” popup or via URL parameter: Mapillary
We’re also working on ways of reducing the number of NeRFs we load and show on the map to reduce data and time required.

2 Likes

Perhaps I should explain what I have actually done there: I have simply opened a fresh browser session, entered https://mapillary.com/app in the address bar, and waited until the cogs stopped moving.
Yes, I am aware that it is a very simple and naive metric but it gives you a certain important natural base line, since it is the first thing users load and see to get going.

Thank you @nikola for addressing the easy and obvious stuff so quickly. Your work is very much appreciated. I think that in the long run the most important thing though is to set a reasonably low hard limit and work from there update after update.

Btw, I have nothing against NeRFs. I like them. It is just that bandwidth and volume continue to be a scarce and costly resource on wireless connections (and many places on earth over wired connections too).

So, I have looked at the web app after the latest updates and things are getting better but I have also found a few things that may need your attention.

  • Please always use progressive encoding on all images. The internet is the land of progressive encoding. :wink: The more pixels an image has the greater the impact for the user. For example, this large 3,550Ă—1,724 px background PNG image
    Mapillary Background
    on the cookie gate is a very good candidate for this. It commands 507.93 KB, which could be less I guess but this is not the fattest :cow2: to slaughter. Additionally, I would go with a standard resolution, like 4K or a 4,096² px square.
  • Some NeRF images bear the .jpg extension but are actually PNG images. For example, seattle-art-museum-1.jpg or chichen-itza-el-castillo-1.jpgCorrect extension but wrong format Though, I am not sure if this is still due to the CDN catching up.

WebP is tempting for its reduced size but I am not sure it serves everybody the same way and provides the best interoperability. So, I am on the fence about WebP. Besides, it is limited to 16,384 px in every dimension (in contrast to JPEG’s 65,536 px limit), hence imho making it unsuited for 360° images going forward.

@nikola Just FYI, the Mapillary sprites atlas served is encoded in R16G16B16A16, which results in a quite huge file. I think this could be a good candidate to be converted into a palette with transparency PNG.

1 Like

Actually nowadays Webp is universally supported by browsers

While this may be true, there is more to consider. The world continues to be filled with lots of legacy browsers, some of which may not be even updateable. Furthermore, only a select number of cameras produce WebP images directly because hardware WebP encoder layouts are limited and costly. And, even if some camera manufacturers would integrate hardware WebP encoders they would have to beat JPEG hardware encoders in price and efficiency first. The reason why JPEG hardware encoders continue to dominate the camera market so strongly after almost 30 years is because they are very simple to implement, cheap to build, and enable extremely power efficient solutions. JPEG is not as bad as many people, especially content creators, think. WebP does produce fewer bytes than JPEG but WebP also demands significantly more compute complexity when encoding than JPEG does, which usually means more power, more die area, and greater cost. Hence, WebP is rather more a publishing format than a production and processing format.

I am not even sure WebP and JPEG share the same color spaces and color sets, so that faithfully converting between these may be an issue in some use cases.

Fair points, but I don’t really understand the necessity for hardware WebP encoders in cameras - might be I’m not familiar with some use cases.

Still, I can propose the following:

  1. As a Quick win - change the JPEG encoder to JPEGLI, which is completely backward compatible and more efficient. In that case the Mapillary team doesn’t need to about different technical aspects of compatibility. I mean the backend JPEG encoder + encoders of the mobile apps and desktop uploaded.

  2. Introduce AVIF image format once it’s supported by smartphones and other hardware in focus.

We’ve now resized all NeRF thumbnails in order to reduce the total size of the NeRF assets downloaded in the web app to be less than 1MB. We’ll also take a look at our other assets and the mentioned formats and encoders to reduce things further in the future.

2 Likes

I do not want to make this thread about image formats (only) because there are a lot of other conventional avenues to take to optimize the web app’s bandwidth load. Changing or adding another image format may have deep running implications to processing and the backend.

As a Quick win - change the JPEG encoder to JPEGLI , which is completely backward compatible and more efficient.

If JPEGLI is capable of lossless recompression resulting in smaller file sizes and relatively easy to deploy on the backend then I would say Mapillary should go for it.
@nikola @boris I am unable to make out whether this is already the case but I would like to see JPEG lossless blurring on a per macroblock level on Mapillary.

Introduce AVIF image format once it’s supported by smartphones and other hardware in focus.

Market forces have not decided on a successor to JPEG yet. However, camera makers and one of the largest smartphone makers (:apple:) seem to gravitate towards HEIF. Personally, I do not favor neither AVIF nor HEIF because they are both very complex to implement and computationally expensive for what you get.

@nikola Furthermore, on a low bandwidth connection (like 64kbps), in order to see an image, you have to click away the Sorry, we can’t find the image you’re looking for. message and repeat clicking on the image (or navigation control) multiple times before the image gets loaded. This is really tiring and things should not work this way.

Thanks for letting us know! I’ll look into improving the experience in case the request for the image times out (I assume that’s what happens in this case due to the connection).

1 Like

On low speed connections you cannot go directly to a specific image either. Hence, something like https://www.mapillary.com/app/?showNerf=false&focus=photo&pKey=1041900054067146 does not work. And, you have no chance to go to that image once the page loads either because there is no way to enter the image key on the page.

You may also want to reconsider your caching policy of styles.css, runtime.js, polyfills.js, and main.js (which potentially causes the above) to something else than Cache-Control: no-cache because most of the time there is really no need to reload these over the wire (or air). Especially, main.js is here the greatest offender with its 5.54 MB! I do understand that you want to make sure that users always load the latest code but no-cache is a sledge hammer. In other words, no policy at all. Please use something like max-age=3600 (or consistently respond 304 Not Modified). This should keep everybody fairly recent and low speed connection users happy. Note, be careful combining max-age with must-revalidate too easily (this has been designed primarily with online banking in mind)!

The same goes for mapillary-loading-logo.svg. no-cache? What are the chances that it is going to change? Having said so, please also consider using immutable on all static resources.

mapillary_sprites.png and mapillary_sprites.json have max-age=900. Really? How often per day do you update these? :laughing:

Oh, and no-store on source-sans-pro-v14-latin-ext_latin-600.woff? Why would we not want to cache a static font file???

The timeout period when loading an image is determined in GitHub - mapillary/mapillary-js: Interactive, extendable street imagery map experiences in the browser, powered by WebGL so we’ll look into changing the value for next release. Image resolutions (and sizes) has increased over the years so this merits a review.

The differences in caching are a result of having a few different sources that serve the web app resources. I agree that the resource caching should be added and can have a longer age as we have ways to make sure users always have the latest version and I’ll add this.

Thanks for your feedback!

Overall, you could say that the app’s caching strategy is inconsistent and requires a review. You can use no-cache on everything but this requires you to consistently respond 304 Not Modified where applicable. However, using max-age can be even more efficient because the client does not have to revalidate a resource on every request until the age passes. So, it is always good to keep an :eye: on the caching strategy, maybe even :eyes:. :wink:

We’ve added caching for the web app resources (js, css, icons, fonts etc.). The cache time is one week as we typically deploy a new version weekly.

We have also reduced the size of our sprite sheet by 45% and reduced the size of our CSS file by half.

The total download size for a first-time user is now less than 9MB!
Thanks for your feedback!

2 Likes

Wow, awesome! :partying_face: These are really great improvements and everybody is going to benefit. Thank you and good job! :+1: I have to test it right away.

1 Like

So far, most of it looks good. However, you may want to work a little bit on that landing page image:

It is properly tagged immutable, has proper max-age, and is in palette color mode. However, with its 507,931 bytes it is quite big and transport layer compression even works against it with 509,810 bytes because PNGs are already Huffman minimized. Here are some improvements you could do:

  • make it some standard resolution
  • make it a JPEG rather than a PNG because you do not actually need all the fine details
  • run a Gaussian blurring filter over it (does not have to be much) to make it sort of more uniform, so that you can take even greater advantage of JPEG’s encoding scheme (JPEG blocks encode most efficiently on uniform or blurry inputs)
  • encode the JPEG progressively
  • for the coolest effect, you could actually update this image weekly :sunglasses:

Well, unless it looks ugly then keep it. :face_with_hand_over_mouth:

Just FYI, I have got it down to 276,905 bytes (ca. 55%) without much noticeable artistic loss. I think you may be able to do even better.

convert -verbose -monitor eTAxYtNd-5x.png -dither none -blur 16x2 -sampling-factor '4:4:4' -quality 50 -interlace jpeg eTAxYtNd-5x.jpg
1 Like