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. Just please keep an on bandwidth for all those people who pay for every megabyte directly out of their pocket.
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.
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. 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
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 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.jpg 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.
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:
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.
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.
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 () 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 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).
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.
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.
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 on the caching strategy, maybe even .
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
Well, unless it looks ugly then keep it.
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.