As eCommerce all around the world has shifted to a predominantly online-first platform the need to provide a high performance website to your users has significantly increased. And on top of that, google has announced that as of May 2021 Core Web Vitals will have a direct impact on page rankings and SEO making web performance even more significant. Come learn the basics of web performance and how it relates to media. Using a simple React based ecommerce app in conjunction with a media optimizing product, you can learn how to deliver the optimal format and fidelity, potentially improving your page rankings.
Faster media = faster websites
Transcription
Okay, so let's go ahead and get started. Welcome everybody. So here we are faster media equals faster websites. Alright, so about us, who will be presenting today. So my name is Marissa Masankai, and I am a technical marketing specialist at Cloudinary. Hi, my name is Pramod Chinoy. I am a solutions architect here in Cloudinary. Hi everyone. My name is Akshay. I'm also a solution architect at Cloudinary. And the two of us, Pramod and I, we typically work with customers to help solve any of their problems, bringing them on board, working with our solution or in general just consulting with them in terms of helping them out in media optimization and media performance. Alright. So what is the objective of the workshop today. So at the end of the session, you should be able to articulate the different core web vital metrics, understand the tools to measure it and relate the audit findings from Google Lighthouse to specific media optimization techniques. So with that being said, here's the agenda for today. So I'll be giving a web performance presentation and in that we'll be discussing core web vitals, tools for core web vitals and web performance best practices. And then Akshay will go over optimizing an ecommerce site, and then Pramod will be discussing Cloudinary techniques for optimization. Alright, and so of course you know we know this is a developer conference and so we kind of wanted to keep in mind media and what are the typical pain points with media and web performance. So with that we kept in mind image resizing and formatting, video manipulation and transcoding, creating responsive images, art direction, so cropping at scale, and hard coding images and video dimensions and having that lack of SDKs. So with that being said, let's talk about core web vitals. So web vitals is an initiative by Google to provide unified guidance for quality signals that are deemed essential to delivering a great user experience on the web, as that is key to long term success of any website. So whether you're a developer, marketer, business owner, web vitals can help you quantify the experience of your site and identify opportunities as to where you can improve. So before I get into what these core web vitals are, let's quickly discuss why you should care about them. Well as of June 2021, so coming up soon, which is actually the new estimated date as of yesterday, core web vitals will begin to roll out and have a direct effect on your page rankings as Google plans to make page experience an official Google ranking factor as a way to deliver great user experiences. So Google has estimated that the full rollout will be complete come the end of August. So page experience will be a combination of factors that Google considers important for user experience, and that includes HTTPS, mobile friendliness, lack of interstitial pop ups, safe browsing, so not having malware on your page, and core web vitals. And as you can see here, Google's own studies show that for pages that meet these thresholds of core web vitals, visitors are 24% less likely to abandon the site. So not only will core web vitals help increase your page rank and help you deliver a positive user experience, but in doing so, you will likely decrease site abandonment. So with that being said, let's talk about what the core web vitals are. So each of the core web vitals represents a distinct facet of the user experience. It's measurable and it reflects the real world experience of a user centric outcome. So the metrics that make up core web vitals will evolve over time, but the current step focuses on three aspects of the user experience, and that's loading, interactivity, and visual stability, which as you can see here are largest contentful paint, first input delay, and cumulative layout shift. Now while the core web vitals are the critical metrics for understanding and delivering a great user experience, it's important to note there are other vital metrics as well outside of the core web vitals. So these other web vitals often serve as supplemental metrics for the core web vitals to help capture a larger part of the experience or to aid in diagnosing a specific issue. So for example, the metrics time to first fight and first contentful paint are both vital aspects of the loading experience and are both useful in diagnosing issues with LCP, such as slow server response times or render blocking resources. And so it's important to note, there is a difference between web vitals and core web vitals. All right, so largest contentful paint. So this measures when the largest content element in the viewport becomes visible so it can be used to determine when the main content of the page has finished rendering on the screen, such as the hero image on a homepage. And one factor contributing to a poor user experience is how long it takes a user to see any content rendered to the screen. So that is what LCP is addressing here. And you can see other common causes of poor LCP are slow server response times, render blocking javascript and css, slow resource load times and client side rendering. Then next we have first input delay. So this captures a user's first impression of a site's interactivity and responsiveness and measures the time from when a user first interacts with the page to the time when the browser is actually able to respond to that interaction. So for example, when you first load a page and you click a button but nothing happens as a javascript is still executing. So this can be definitely a frustrating experience. So although largest contentful paint measures the time it takes for content to visually render or paint on a page. It does not capture load responsiveness or how quickly a page response user interaction like first input date delay does here. And next we have cumulative layout shift. So this measures the instability of content by summing shift scores across layout shifts that don't occur within 500 milliseconds of user input. So it looks at how much visible content shifted in the viewport as well as the distance the elements impacted were shifted. So as layout shifts can be very distracting to your users, you of course want to avoid this. So imagine you've started reading an article when all of a sudden elements shift around the page that throws you off and then you have to find your place again. So this can be caused when visible elements are forced to move because another element was suddenly added to the page or resize leading to a very frustrating experience for your users. And common causes of poor CLS are images without dimensions, ads, embeds and iframes without dimensions and dynamically injected content and actions waiting for a network response before updating DOM. All right, so now we're going to get to the tools section. So now that you've learned what the core web vitals are. So let's talk about a way to how you can actually measure these metrics. So let's start with Lighthouse. So Lighthouse is an open source automated tool for improving the quality of web pages. You can run it against any web page, whether it's public or requires authentication. Lighthouse audits for performance, accessibility, progressive web apps, seo and more. You can run Lighthouse in Chrome devtools from the command line or as a node module. You just have to give Lighthouse a URL to audit and then it will run a series of audits against the page and then in turn generate a report on how well the page did. So from there you can use the failing audits as indicators on how to improve the page. So as you can see in this screenshot, this site is scoring in the green zone, which is where you want to be ideally, but for any time you're not in the green, Lighthouse will give you actionable advice as to what you can do to help increase your score. And you'll see what this looks like in a little bit later in the talk so just bear with us for now. The next tool would be WebPageTest. So WebPageTest is a web performance tool that uses real browsers to access web pages and collect timing metrics. So by entering all of the relevant information you see here in the screenshot, you can test your user's real experience with global locations, devices and browser versions. And this allows you to correlate your user's visual experience to the technical measurements of your site. Now just like Lighthouse, you will get to see what WebPageTest looks like in action shortly. And the next we have is Report Web Vitals. So for all of you react developers out there, you may find this useful as you can actually use a Report Web Vitals function to get your core web vitals metrics without having to use any external tools. So these metrics are pooled by leveraging a third party library called Web Vitals, and Report Web Vitals is fired when the final values for any of the metrics have finished calculating on the page. So you can use it to log any of the results to the console or send it to a particular endpoint. So for example, you can send the results to an analytics endpoint, and that can help measure and track real user performance on your site. All right, so now that we've discussed core web vitals and how to measure them, let's talk about what you should take into consideration to either keep or place your scores in the green zone. So the first thing you can do is deliver optimized assets. Delivering optimized assets is one of the best ways to affect your web performance and let you deliver positive experiences to your users. Now this may seem like a no brainer, but when you think about delivering an optimized asset, what does that mean? So let's take a look. So let's first talk about formats. Image formats can significantly affect page load time and bandwidth and in turn directly affect user experience. So modern formats like WebP can reduce image sizes by 30% naturally leading to faster page loads, more site engagement, and higher conversion rates. Taking a look at the three image formats here, you can see that converting the original JPEG into a WebP already gets you a 26% savings, and converting to AVIF gets you a whopping 61% in savings, showing just how valuable it is to serve optimal asset formats. Now keep in mind this of course is all easier said than done. A common developer task is to pick the optimal formats for various scenarios based on the content and the viewing device or browser. So for example JPEG for captured photos or for faster loading and PNG for illustrations or pictures with a transparent background. And for Chrome, Safari, IE, and Edge browsers, you must take into account for additional logic for modern formats like WebP and JPEG XR. So as simple as the requirements for browsers and format compatibility might be, manually computing the format logic for a large volume of assets is complex, likely inefficient, and certainly no easy task. One way to take the pain out of delivering assets in their optimal format at scale is you can easily use features such as an auto format algorithm. So auto format algorithms can look at the requesting browser and content of the asset to determine what the optimal format is and auto convert it on the fly if necessary. So features like this can also help you stay ahead of the curve by offering modern formats such as AVIF displayed here, and this not only keeps you up to date but also requires less maintenance on your side. So just to give a quick example of what this auto format algorithm can look like, I will show you real quick. Let me take this out of full screen here. Let's see, desktop. So you can see in this URL, I'm using a Cloudinary URL and we're setting it to an AVIF format, but I'm going to change that back to the original of a JPEG. So I already have it, our width set to be 1,000, and I'm simply going to add an F auto here to the URL, and that's going to use that auto format algorithm to take a look at the content of the asset and what browser I'm requesting it from. And then it's going to choose the best format. So I'm going to click enter here. So you know what, let me show you as a base just for comparison. I'm going to take out the F auto just with 1,000, and I will show you here in the media info. So you can see that we do in fact have a JPEG image, and it's at 135 kilobytes in size. So then, by adding in the F auto here. I'm now getting You can see that it knew that I was requesting with Chrome and I now have an AVIF image, and we're now at 51 kilobytes in size. So just by adding in this algorithm in the URL, I didn't really have to do much at all. And I was already able to deliver that optimal format. Okay. So next is quality. So a common concern for image and video edits is degradation of the visual quality. How can you easily determine just how much to compress the asset without sacrificing any visual quality. So, precise adjustment of the compression level complemented by fine tuning of the encoding settings can significantly reduce file size without any noticeable degradation. The hardest part here is that no single optimal setting exists because it depends on the compression algorithm and the assets format and content. So indicating that setting a default quality level amongst all your media is pretty suboptimal. And looking at the three different versions here, you can see all three look quite similar and the difference here is actually in the file size. So although definitely not every image out there is going to be able to be reduced to a quality level of 20, but you can see just how much savings you can have if you have a way to calculate this. So a great way to do this at scale is by utilizing a quality algorithm to help determine how much you can compress the asset without sacrificing any of that visual degradation. You can utilize an algorithm to determine the optimal quality by taking into account the content of the asset and just how much you can compress it without the human eye noticing. So next we'll take a look at size. So although it may seem pretty straightforward to deliver assets at the desired size, I'm sure we're all guilty at one point or another resizing assets on the client side. And we want to avoid doing this at all costs because doing so is a sure way to not only increase load time, but also leads to wasted bandwidth consumption. So this will be discussed later in the workshop, but you'll see there is an opportunity section of your Lighthouse report in which lists all images in your page that aren't appropriately sized, along with the potential savings in kilobytes, as this is an essential best practice. So as we all know, website changes can happen quite frequently and creating asset variants can get time consuming. So, for example, you have a 4000 by 3000 pixel image for a product you want to list on your e-commerce site. On your site, however, you need to show a much smaller image of this product. So it could be a 200 by 300 pixel image on the product listing page and an 800 by 1000 pixel on the product detail page. So in that case, you want to make sure you scale down the original image to these dimensions before rendering it in the browser. So this way, not only will you be able to load the smaller asset faster, but you will also be able to save on bandwidth. So looking at the difference here, you'll see that even if you first loaded in a derivative that's smaller than the original, it results in consuming almost double the amount necessary after resizing it still. So you definitely want to think twice about what it's costing you to load in these larger sized assets. And just to hit home with this, with all of this being said, you want to make sure you're resizing your assets to the exact size of what will be displayed on your site or app. As this doesn't mean resizing via css or html, you can see in this snippet here, setting the width to 300 pixels and the height to 200 pixels is something that you definitely want to avoid because you want to resize your images on the server side and not the client side. So you can see here in this Cloudinary URL that I have, I'm requesting an image that has that width of 300 by 200 and is already resized on the server side. So doing so will result in faster loading times, decreased consumption of unnecessary bandwidth, better user experience, and help you maintain control of the focal point when cropping. And that brings me to my next point. So we have utilize AI for smart cropping. So smart cropping is not a web performance best practice, but it certainly comes into hand when it comes to resizing. One of the biggest pains about resizing is making sure that the area of interest always remains as a focal point. So you can see in this image here, the girl gets cropped out when the aspect ratio is changed as the resizing stays focused on the center of the image without taking into consideration the content. But when utilizing an AI algorithm, the automatic crop is right on target as one would expect. So this helps you eliminate the need to manually verify the area of interest is always in focus when resizing each of your assets because for those of you who may be handling media at scale, you definitely want to know that you can resize your assets within your workflow and not have to check everything and be able to, you know, lean on AI to make sure that that area of interest is always the focal point. Next, we have delivering assets via CDN. So what is a CDN? So at its core, a content delivery network is a network of servers linked together with the goal of delivering content as quickly, cheaply, reliably, and securely as possible. So in order to improve speed and connectivity, a CDN will place servers at the exchange points between different networks. Popular CDN companies that you may have heard of are companies such as Akamai, Fastly, and cloudflare. And while there are many benefits to using a CDN, a few of the main benefits are improving website load times. So by distributing content closer to websites, website visitors by using a nearby CDN server, visitors experience faster page loading times. As visitors are more inclined to click away from slow loading sites, a CDN can reduce bounce rates and increase the amount of time that people spend on the site. So in other words, a faster website means more visitors will stay and stick around longer. Then we have reducing bandwidth costs. So bandwidth consumption costs for website hosting is a primary expense for websites. Through caching and other optimizations, CDNs are able to reduce the amount of data an origin server must provide, thus reducing hosting costs for website owners. And then we have increasing content availability and redundancy. So large amounts of traffic or hardware failures can interrupt normal website function. Thanks to their distributed nature, a CDN can handle more traffic and withstand hardware failure better than many origin servers. And then, of course, this helps improve your website security as well. And then next, delivering assets over a multi CDN. So as you can imagine, multi CDN is, as it sounds, it is the combination of multiple CDNs from different providers into a single network. So taking a look at the image here, you can see there are three different CDN providers with all of their worldwide locations shown. You can see that by using multi CDN, you get a lot more coverage for your users than you would if you were using only one of these networks. So this gets you all the benefits of using a CDN, as I mentioned in the last slide, in combination with the following benefits. So delivery of content via the fastest route. CDNs are selected based on what will be fastest for the individual user. So if there are multiple providers in one region, content is served from the fastest one. Then you have decreased chance of downtime. If a CDN provider's network goes down, traffic will be routed through another provider. So this helps you achieve 100% service uptime. And then we have, of course, access to a larger network of nodes. So you get access to an increased amount of CDN locations, which wouldn't have been possible with just using one provider, as you can see here. And as CDN data centers can handle many more requests than a single server, this helps to relieve the load on your main server. So I know that was definitely a lot to take in at once. But now that we're in the know about core web vitals, what performance best practices, how you can measure them, let's see this all in action. So I'm going to hand this over now to Akshay. Thank you, Marisa. Hi, everyone. I just shared a bunch of links to the code snippet, the GitHub, and the sample application that we're going to use. So let's get started. So you can use that as a reference while I go through the talk. OK, so Marisa covered a lot of different things in terms of what are the kind of pain points we as developer face? Why does performance even matter? And what are the other things that you need to take care of while delivering the resources? So it's not just taking an image and converting it to different formats. There are a lot of open source tools that can do that for you, but then also making sure that when you deliver, it is the fastest when it reaches the user. So for this trial, what we have done is I've picked a very simple template. So this is actually an e-commerce template site that was built by Baomic. It has essentially three different sections, a typical home page, a listing page, and a product detail page. We're just using this as a sample. So using that, what I did is I just updated it a little bit to make it look like what you would normally see now. The current trend is on the home page, you'll have this large video which is playing as a background, which is looping. And then you have links to different sections, typically again using some kind of rich media. And then your category and product detail page. We are not really covering the checkout process because by the time you hit there, the number of media resources actually comes down. And most of the performance optimization tends to be focused on these three kinds of views. So that is where we are going to restrict our talk for now. So what I've done is I've actually hosted both the modified app and the customized Git repo on the GitHub. And then we are also hosting it on Netlify. So you can actually go try it out for yourself and then run your own tests to see what other kind of recommendations it comes up with. And then the idea there is you can see those recommendations and try to figure out how you would actually try and fix those problems. So that's why we kind of left it there. So what are the customizations? So the main thing I've done is just added a video background. Obviously, that's number one. And what I did was I swapped out the images, the original app used. And then within the public folder, I've added this workshop subdirectory. And then there is a bunch of images there. Apart from that, there's not a whole lot of change in here. Instead of using whatever images it came with, I just switched it around, switched it around. And I said, just use these images. Now, note here that I've only done like replace. So I have not taken care of adding any performance optimizations on the baseline code. So if you notice here and if you try to figure out what Marissa was trying to tell us, those optimizations will not be here. We'll see it in another app that we'll see in a bit. Apart from that, again, you know, the banner, this is where I've added the video. It's a simple HTML5 video tag. All I'm saying is it's going to be in autoplay muted and it's just an MP4 video. There's nothing special in there. And lastly, there is just one style sheet. Not a big thing. Not a big thing. It's just forcing the video to be played as part of the background and it doesn't kind of play video by itself and drop everything down. So that's that's the main optimization or customization rather that I've done as part of this change. So that is the customization. Now, with that, you know, from starting from there, I'll walk you through how to take it to this optimized app. Now, in this optimized app, it's not completely optimized. We have left some things for you to work on. So in this app, as you will see in the screenshots, there is nothing that has changed in the look and feel. What we have done is instead of using images coming from within the app, it's actually going to come from Cloudinary. So one of the points that Marissa mentioned is you need to use CDNs. Now, in this case, when we are delivering from Netlify, we are actually using Netlify CDN already. But you will notice that there is still optimization we can gain when we actually convert it to better formats and then try to cache it even better. So that is something you'll notice as we run our metrics. So the rest of the talk is how to take from that baseline app to the optimized app by measuring different things, looking at what are the recommendations that come out and then making those changes. So let's start with the baseline. Since we have been talking about web page test, I use web page test for the baseline. And luckily in my case, since it's an app that doesn't need to be protected by passwords or there's no restriction on exposing it to Internet, I could easily run this. So take the Netlify app, put it in web page test, get the result. So let's take a look at that web page test. So here we are. So if you notice here, some of the things that stands out, we spoke of largest contentful paint. It's actually very slow. Now, this is a test I've run with native connection, so no network throttling on a regular desktop Chrome browser. And this is still the performance that we are seeing. There is a lot of cumulative layout shift. And the number of bytes in, that's the total weight of the page, is actually quite high. It's about 22 KB. This may not be completely representative. So in a real world, when you would have published your website, I'm sure there is some steps that goes in where you would have actually cut down the quality of the images, optimize the video and then made it better. So the total bytes in a real world would be not as high, but it would still be higher than what's necessary in most cases. If these, you know, optimizations are not done. So that's just the static point. Now, in web page test, like Marissa mentioned, you can actually also request for Lighthouse audit. So I did that. So when you run the test, just go to the Chrome tab and say Lighthouse audit. And what web page test does is it simulates a 3G fast connection and then tries to run the test. I think on a Nexus device, if I'm not wrong. So with that profile, this is the result I get. Pretty much everything is in red. The first contentful paint, speed index and largest contentful paint. So this is where we were trying to focus on. But even the cumulative layout shift, that's also quite high. And what are the recommendations? So the recommendations we get is that, hey, you're not serving right sized images for these particular images over there. And then the other thing that is also standing out is that we don't specify the width and height parameter. Now, what does it mean? So this image here, I could have actually brought it down as a very tiny image. Instead, what I'm doing is I'm actually getting this image. So it's huge. And that is a problem. It's not common to see this kind of a thing happen. And many a times, the development teams do put in proper workflows to catch this. But at the last minute, someone introduces an image which kind of gets pushed out in a hurry and then it's forgotten. So those are the places where this kind of an issue crops up. Now, these are the recommendations that we see only for the home page. Later on, I'll show you what happened on, say, the category page. So you can just click on any of the category name there. It comes to the same category page. And then if you click on the product name, it comes, takes you to this product page. Now, the thing to note is it's just a dummy page here. So if you click on these items, it doesn't really change the product, which is what you would actually expect in a real e-commerce shop. So those are the different recommendations that we have here. And the other thing that you will also notice in the waterfall, if you go down, is this is huge bar. That's basically the MP4 being downloaded. And it takes about seven seconds. The other thing I do want to call out that was introduced, I think, sometime last month, is this dotted lines. What it says is every time there is a dotted line, a layout shift has occurred. So in this case, there were three different layout shifts. And that's why our cumulative layout shift score is actually kind of bad. So those are the different recommendations that WebPagetest has given us. All that is great. But when you're working on a site that is yet to be launched, you cannot really rely on WebPagetest. So what's the alternative? You can just run the same Lighthouse audit on your Chrome browser. So just go to developer tool and look for Lighthouse audit. I did exactly that. And when I ran the test, this is where it started out with. So on the baseline code, I got a score of 62. Now, the thing that I want to stress, I guess, is while you're doing this, it's best to define or decide on one system where you will be taking these measurements so that they are comparable. So if you keep changing the system where the test is run, you'll see different numbers that kind of makes it hard to compare. So in this case, I've taken all the measurements on my own local laptop. But if you notice here, the recommendations are similar. First content, full paint, largest content, full paint, they're red. And similar recommendations, we need to properly size the images and then we need to have the right width and height. There is one other recommendation that did not show up in the screenshot, which is adding a pre-connect. Now, as part of the optimization, what I did is instead of hosting images from within the app, I'm going to start using Cloudinary. So the images are going to come from a third party domain, res.cloudinary.com. So when the moment I introduced that, it complained of a new thing, which is add a pre-connect hint. So what I did was I said, OK, let's start optimizing the home page. And let's start with the simplest one, which is just add this pre-connect hint on the page. Just adding that took me from 63 to 65, so three point gain on just adding that. So where did I add that? It's actually on the home page. So this is my baseline app. Let me switch to the optimized app. And in the optimized app, let's go to index.html. So this is where I added that. So all it does is it says, hey, browser, before you even start parsing and you hit a point where you actually discover an image coming from this domain, don't wait for any of that. Right away, go do a lookup for res.cloudinary.com. Establish a connection, a DLS connection, so that when the browser actually looks or reaches an image, it can immediately fire a request saying, get me the image. So just basically cutting down that initial handshake time. So that is what this specific recommendation did. So that's optimization one. Then after that, I did a bunch of other things, which is trying to resize the image so that it fits the display. Then for each image, adding a width and height attribute and then adding the right format and quality and right quality for video. So let's actually take a look at these changes. So before getting there, one quick call out. So you will also notice that this javascript is extra as compared to the baseline code. This is something that I'm going to use as part of the product gallery page where we actually render a real widget. In real world, again, we would have made sure that this javascript loads only when that view is reached. But just to keep it simple or to give you an opportunity to actually further optimize, I just put it on the index page so that it just loads at the start. So that's one. The next thing is our home page. So in the home page, what I did is I said, OK, now start loading images from Cloudinary. Now, for this, what I did do is I've actually installed a Cloudinary library. So that's the Cloudinary react SDK. That's this one. And we are looking for two components, image and trust. And I'm using those here. So I'm saying, hey, get me an image. And this is my account name. So think of it like your user ID. And what is the object within that? So this is basically the parts to my image and with the folder structure. So I've created a folder called Workshop and here is my image. And I've given the width and height parameter so that I need the Lighthouse audit finding. And then I also give some transformation just before getting there. One thing to note, this width and height, if you notice, I'm only seeing width, some value, height, some value. I don't specify pixels or inches. And the reason is these values are actually going to be used by the browser to only determine the aspect ratio. These are not the actual size. The actual size will be based on whatever the container, the browser figures out at the time of rendering. That's the reason why this recommendation is important. So if you provide that, your browser knows the aspect ratio and it can kind of keep a hold on that area so that layout doesn't shift. And that's why this kind of helps you in preventing layout shifts and getting a better score on CLS. So that's that. The next thing is the transformation itself. So on the home page, I showed you there are those images for different categories. They don't need to be full size. So I'm bringing it down to 350 by 250 pixels. That's this transformation. And there's actually a typo there, width. And the quality. Now, this quality auto, what it does is it says, hey, my user, whatever image quality works for my user, let's try and get that based on this specific image. In a normal world, you'll see a lot of recommendations saying, hey, if you're if you have a JPEG, bring it down to quality 80. It works in most cases. It's a good baseline. But in a lot of instances, you can actually go way beyond that. You can bring it down to, say, quality 30 or quality 40. It all depends on the actual image and whether a human user would even notice the difference. So that is where this is going to help you out with. So that's this specific parameter. So that's how we are doing the transformation here and then bringing it down in size. The other thing is, apart from just the image, is the video. Now, what I've done with the video is I've said, hey, same thing. Use the Cloudinary library. And instead of using the image component, I'm using the video component in here. And I'm showing you a slightly different way of calling it, where you can say, provide the context. So I'm kind of pushing the username component to a component by itself. And then I'm saying, hey, here is a poster image for my video and then the same set of criteria. And then finally, I'm saying, for the video, apply a quality auto. Now, using this, we are able to render the background video. Another thing that you may want to think through is, hey, I have a bunch of images. All of them have the same kind of things. I'm always saying, what is this, height is this, and I also need to put the same thing in my image tag. So why can't I build a custom component that just does this, where we only pass the image name? So that's totally acceptable and that is highly recommended. So we could have done something like home page image as a custom component, which basically embeds all of this information and it only takes this as a property. So you could have just sent it as a parameter and it just renders in the right fashion. That is actually a very good way to take this forward. So we could have done something similar for the home, similarly for the category and for the product pages. So those are some of the optimizations that we have done as part of this sample app. Now, this here for the right format, I think there was some chatter on the chat saying, hey, how do we determine the format? As someone pointed out, the way we do it is based on the request coming in, we detect what is the user agent and what are the accept headers. Based on that, we will be able to determine what's the right format. So at runtime, we are actually figuring out what needs to be delivered. It gets created and sent down. If you are not using Cloudinary, you would have to build a logic where you would recreate these formats, have them ready. And then when the user comes in, you just deliver the right format. The thing to note there, though, is you would need to make sure that all of them have similar names. And then if you are also delivering using your own CDN, you need to make sure that it understands that these different formats are different files, actually. So the URL may remain same, but what gets delivered is actually different. So it gets into the mode of using very header and so on. If you want to get into that, those nitty gritties. But that is what it is. So with those optimizations, what happened? So on my local system, again, when I ran the next Lighthouse audit, the first contentful paint has improved. It's all orange. And then it's doing much better. And what are the recommendations? Now, the recommendations here have changed. They're mainly not to do with the media object. There is one here around preload largest contentful paint image. But everything else seems to be around javascript optimization. So the scope here was around media, so we did not really touch anything to do with javascript. As part of your exercise at a later point, you can actually try preloading the LCP image. So when you run the audit on WebPagetest, it will tell you what is that image. And then you can try putting a hint in the index html for that image and then see if that helps improve your score. So that is how we have taken from a default app to a media optimized app. And then when I ran the test initially, I also noticed some recommendations where it said deliver in the right format. And then I figured out the head of footer images had been missed out. So I made those changes and then I got a performance increase or a score increase of one. Not a whole lot. It's still worth it. All of this is on local system. So what happened when I ran the same thing on the public WebPagetest? So in here, the Lighthouse score definitely has changed. It has jumped to 54. And we'll actually see what that is. So let's go to the WebPagetest. Here we are. So this is the optimized app. This was the non-optimized app or the baseline. And if you just do a compare, you'll clearly see the Lighthouse score has gone from red to orange or yellow. And these metrics, the largest Contentful Paint and Cumulative Layout Shift, that has actually dropped. So in here, the largest Contentful Paint is now 0.719. And Cumulative Layout Shift has come down from red to an orange. Not the best. Some improvement. And the bytes definitely has dropped. It's come down to 7 MB instead of 21 MB. Now, the cool thing, well, let me just show you the Lighthouse recommendation as well. Again, it says, what is this particular image, which is being considered as LCP? So it's actually not the video. It's the very first category page image. That is what is being considered as the largest Contentful Paint. So that's the one that it says you need to optimize. Now, the cool thing about web page test, again, is you can run comparisons. So here is my baseline and here is the optimized version. And it shows you this filmstrip view saying, hey, what happened and why was there a layout shift? And what is it that caused all of these problems? So if you notice here, when we start out, it starts with the blank, obviously, and then it loads the header footer and then it's actually loading the component that comes in the middle, the paint container. So there is a layout shift here that's shown by these dotted lines. And there is one more that happens later, I think. It's actually hard for me to figure that out. I'm still trying to debug. I disabled a carousel. So that's what I think is causing that issue. But anyway, so in the case of the optimized app at 2.8 seconds, if I go in here and show the waterfall, it says at 2.8 seconds, all the resources that I need for my optimized page is actually done. So pretty much at 2.8 seconds, we are done with this page as compared to the other one, which says it is going on. The weird thing here was it's actually loading just one category page image and then it says it's 99 percent done. So something that you need to actually eyeball and try and figure out. The other thing that also shows up is your visual progress is improving with the optimization. And then pretty much every other metric has actually done better here. So the CPU time has come down. So you gave the right format to the browser. You gave a smaller image to the browser, so it has to do lesser work. So that's the lesser CPU time. Similarly, the largest content full pane came down, but more importantly, even time to interactive. Now, this is also a metric that Google is going to track and this is directly impacting your user. So they think the page is loaded and they try to click and nothing happens. That's frustrating. So that's actually come down. We already saw that cumulative layout shift has come down. Here we are. But the best thing is this. So the byte reduction that you notice is huge. javascript bytes has increased and that's because of the product gallery code that I added. But everything else, the image bytes and the video bytes has definitely come down. So those are all the different kind of optimizations that we have done on just the home page. So let's take a look. So with web page test, again, you can create this comparison video. Now, the place where this kind of helps out is if you make it part of the build process and then you have a performance budget, you can track it against that. So there are known modules that help you do that where you can define the performance budget as a JSON and then it kind of aborts even the check-in if you are able to connect it to a lighthouse audit or to a web page test private instance and then fail the audit or fail the build itself if you don't meet those requirements. So those are some of the possibilities that you have just using these tools. The other thing I wanted to show is since we have a site which is composed of a navigation from page to page, you need to compare that behavior. Just going one page at a time may not really help because a real user may not really land on the category page. 80% of the users, suppose, go to the home page and then they go to the category page. So by then the react hydration is done. They are going from page to page. So a lot of caching kicks in. So what is the kind of performance that you see with optimized page without optimized page? So that is a comparison that you may need to run. So you can do that as well. So let's take a look at how that is done. So here we are. So that's this one. Here we go. So here is those pages. Now let's just go into one of them. So when you run the web page test, you can actually say it's a scripted test. And when you do the scripted test, you can actually specify these three steps. So let's just check if it shows the script. I'll put the script that I used on Discord at a later point, but it's very simple. It says, like, navigate tab URL 1, navigate tab URL 2, navigate tab URL 3, and so on. So that is what is going in as three different steps. So that gives you all the metrics that's captured on each of the steps. And you'll notice that even with the baseline app, there's a huge difference between just going to home page and going to the next page, because the number of resources being loaded obviously drops, and that basically translates to a much better number after the home page. And that's very common for single page app frameworks, whether it's react or angular or any of them. So that's something that you can definitely try out. Cool. So that's something I wanted to call out there. Now, I understand that we have walked you through a lot of different things that can be done, and we did not really have, like, a hands-on coding session. So to help you with that, we have created code sandboxes. One sandbox basically is for the product gallery widget, where it tells you how to build a gallery. And the other one is this is showing you what are the different kinds of optimizations that are possible. So let's take a look at that. Now, that are possible and that you may want to use. So, you know, you want to use a react SDK and then you want to embed a very simple Cloudinary image. How would you do that? So that is this one. And you want to add auto-format and quality. That's this one. And, you know, like it goes one step at a time saying, OK, now that you have a very simple image with auto-format and quality, and then you want to add some cropping, you want to give a 350 by 350 crop, how would you do that? So it takes you one step at a time kind of walking you through all the different options that are there. And then we show you some of the other things that we discussed. So one of the things that Marissa mentioned was the AI based cropping. So this is an image where the subject is kind of off center. So they're towards the right. And when we do a crop, it's kind of cutting her off. So her head and head is cut off. Whereas if you were to do a smart cropping, it kind of figures out that, hey, a human user is not really interested in the blue wall, but they want to see the person. So it brings them back in focus. The other thing is like converting your video. So in here we have a landscape video. Maybe you want to post it on Instagram. So you want it as a square. How would you do that? So that is the simple case there. And then you have some really complex use cases. This may not be useful for every single page of your website. It could be specific email campaign and so on. This is one of those things. So you may have seen something like a color pop effect where one thing is shown in color and everything else is black and white. That is what I'm trying to show here. So took an image, removed the background, and then tried to overlay one image on top of the other. So it gives you this effect where it's kind of telling you that everything else apart from the subject is black and white. But it's actually not true. It's the same image you're being used twice. And then we saw, you know, for an email campaign, you may want to add some text overlay. So that's an example there. And to address the common use cases, like every home page image needs to be cropped in the same parameters. We actually can do much better than specifying width and height as part of either the code directly on the page or as part of your custom component. Instead of doing either things, you can kind of externalize. You can say, hey, you know, we'll call those images as, you know, home page images. And the definition of what that means can reside on Cloudinary. So that's what we call as a name transform. So I'm showing you an example there. So I'm saying the name is crop 400 by 400 and you can give whatever name you want. So it could have been like crop home and it would do the same job. And then when you're doing those crop and then you want to also kind of overlay one image over the other, how do you do this? So the typical use cases, you have an image and then you want to put on sale a banner called on sale towards the top right, top left, wherever it is. How would you do that and how would you do it at scale? So those are some of the examples that I'm showing over here. So you can take a look at these sandboxes and then try it out for yourself. So those are all the different optimizations that I have done for the code base that we are using as part of this workshop. Now, Pramod will actually take over and he will show you a lot more of these optimization strategies and how you can make your site better. Thank you, Akshay. Let me share my screen. Perfect. Based on what we saw from Marissa's talk on why a media optimization matters and what Akshay was able to prove very quickly using web page tests that quick media optimization is indeed possible. We will be discussing two strategies when it comes to media optimization. But before that, let's look at few scenarios or challenges you as a developer might encounter with your media optimization journey. So scenario one, right. So let's say your web analytics team reports that 70% of your end user traffic is coming from Apple devices, the remaining 30% from let's say Android or Windows. And most of the Apple devices do support a retina display. So in that particular scenario, you might want to make a decision, right. Hey, do we serve some sort of a high quality image because 70% of the users are coming in from retina devices or do we not serve high quality images? Because there are few devices that does not support these high DPR images. And why just to take a performance head, right. Or you might even think about a complex logic to determine that, hey, it's a retina device, let's serve high quality image. And this is not a retina device, let's serve maybe a low quality image. So basically the question is, do we compromise on performance for quality? Or do we compromise on quality for performance? If you do that, then users on let's say a retina display might be able to see some sort of pixelated image. So image quality is something which you definitely need to consider as part of your image optimization strategy. If not, your end users might end up looking at a pixelated image. Scenario two, say you are dealing with responsive web design site, right. How do you handle images? Do you download the entire high resolution image from the server and do you resize it on the client side? Yes, it definitely works. But is there a real need to download high resolution image, especially in scenarios where you might just show one tenth of the entire image, right. So that's another scenario to think of. And also, even if you do download the high resolution image, how do you make sure art direction is taken care of? Basically making sure that the main content of the image is indeed in focus and is not chopped off, right. So, for example, in this particular example, the image is kind of chopped off bottom and on the top. So you need to make sure that art direction is handled. And also, if responsive web design is not properly implemented, your end users might be looking at a squished image like these as well. So when it comes to resizing, these are like scenarios that you might want to consider. And then definitely, as we saw from Marissa's talk, image format, serving image format is definitely important when it comes to visual performance, byte saving as well as for Google as well, right. Because Lighthouse clearly shows you that, hey, you're not serving the right format for this particular browser. So image format is something which is which is something you need to handle. And if not properly handled, let's say the image format is not currently available for the browser to choose from, then they might look at some sort of an error message like this, where they say that the image format is not supported. So for me, the three key pillars when it comes to media optimization are making sure that we are serving the optimal quality for different browsers. The resizing is happening appropriately. We don't download like full res images. And also when we do download a cropped image, making sure that the art direction is taken care of and then definitely the format of the image that you're downloading. So these are the three key pillars. And in addition to this, right, there are a few other challenges that you might face related to scalability. For example, these web vital metrics, right. Just three or four days back, Google announced that they are changing slightly the way they are calculating the cumulative layout shift method. So future changes to these core web vitals is something which you definitely need to consider in when it comes to media optimization and also different image formats, right. So I think Jordi in the chat had a question. How does Cloudinary handle image formats compared to just using different source sets? Again, it's the question of scalability. In the future, there might be newer formats. There might be formats that is supported only by a certain set of browsers. So that's something which you may want to handle inside your code when you are creating those source sets. So that's another scalability related challenge. And then when you have media coming in from different locations like S3, GCP, image hosting service, on-prem data center, etc. One of the key criteria that you need to consider is the image file structure because Google image seo ranking definitely wants you to have a consistent image file structure. If not, then you will be getting a poor ranking when it comes to the image file structure as well. So these are some of the challenges when it comes to the scalability when handling media. So now let's take a look at two techniques when it comes to media optimization. These two techniques are the Cloudinary way for media optimization. This is because there are like tens and thousands of developers out there who are using Cloudinary for seamless and high performing media delivery. So let's take a look at those two approaches. So technique one, this is where we will be treating media as code. So as we saw earlier from Akshay's example, he did use URLs that would include some sort of transformation. In this particular scenario, a simple delivery URL for a sample.jpg image where we are basically asking Cloudinary that, hey, give me an image of quality 60. So we do have SDKs, client side as well as server side SDK. So based on some predefined rules or logic, you can decide what is the best quality of image to be served for this particular user. Similarly, in addition to quality, we have auto formatting, auto width. For example, I can quickly share this URL with you. Let me just share it in the chat. And you can modify the quality parameter. So instead of underscore auto, you can specify like underscore one, underscore 10, all the way up to 100. So basically, you are specifying the quality parameter. Similarly, for format, you can either let Cloudinary decide that, hey, give me the most optimal format. Or you can ask Cloudinary, hey, instead of JPEG, give me like a WebP. So similarly, I think there was one question from Jordy around how Cloudinary handles image resizing. So similar to QAuto and FAuto, we also have WAuto as part of our responsive solution. So basically, you would be including WAuto in the delivery URL and our responsive solution will take care of the required width and height of the image. Again, there are SDKs, client as well as server side, for you to quickly handle these different scenarios. And you can see here in one liner itself, we were able to handle the three key pillars of media optimization. Format wherein you don't have to specify multiple sources for multiple formats for the browsers to choose. Quality is automatically taken care here. And same goes for width as well. Here I have 500 as the width. But again, with responsive solution, you can just use WAuto and we will resize the image for you. Another example, based on your responsive web design, say you want to crop this particular image. Let me just share this image with you in chat. So let's say you want to crop this into 200 by 300. And if you click on the link that I just shared in the chat, you will be able to see the cropped image, something like this. And you can see here, yes, it is indeed 200 by 300. But the main content of this image, which is the basketball and the hoop, that is kind of chopped off on the left. So this is what happens if at all resizing is done incorrectly. The main content of the image might lose focus. So with Cloudinary, you can just add another parameter. So let me just share that URL as well. So in addition to the cropped delivery URL, you're just adding a parameter called as Gravity Auto or G-Auto. And when we perform the 200 by 300 crop of this large image, we'll make sure that the main content of that image is in focus. And again, there are SDKs that you can rely on, where you could basically say that Gravity equals to Auto to generate these sort of cropped images. And what you saw previously with Auto, Format Auto, Q-Auto, these are the same optimization that Akshay had previously applied when he was performing the optimization to the baseline app and how he was able to improve scores in Lighthouse. So this is technique one, where we are treating media as code. Basically, using the SDKs, we are able to generate delivery URL on the fly and Cloudinary will handle all the optimization aspect. So the technique two is what we call a media optimizer. So this is used in scenarios where you want to handle the image optimization logic outside your front-end code. So, for example, previously with media as code, you could see all these different optimization like quality, format, auto, width and height, everything in the delivery URL. But there might be a scenario where you wouldn't want to expose all this information. In that scenario, you can, let me just go back, you can create something called as a delivery profile as part of the Cloudinary platform. So here I have a delivery profile called as Cropped Squared 300. And this will include all the transformation that you're seeing over here. So this is a custom delivery profile that you can create as part of the Cloudinary platform. So let me just show it to you over here. So here I'm saying that, hey, create this delivery profile, Cropped Squared 300, and use a transformation called as Cropped Underscore 300. And this has been defined again as part of the Cloudinary platform where we are saying that resize 300 by 300, enable quality auto, enable quality and format auto. And I want to save this particular transformation. So all the image handling logic is not included as part of your code, but it is handled here in the platform where you can define delivery profiles such as these. And Cloudinary will handle the transformation for you. Let's say even in your existing code, right, if you have some sort of templatized or custom components that defines that, hey, this is the media, sorry, this is the product page. This is the category page. So those sort of meaningful names can be brought into these sort of part prefix for creating different delivery profiles and appropriate transformation can be applied. For example, all the category images need to be of the size 300 by 300. PDP needs to be of the size 1000 by 500, for example. So those kind of transformations can be defined. So here are a couple of delivery profiles. And what you could also do is here we have built a very basic app. So you can actually go to this app and upload an image from your local machine or even from some remote sources such as web, camera, Facebook, etc. But to keep it simple, I'm just going to upload an image from my local machine. And once it gets uploaded, you will see this table wherein you have different delivery URLs for different delivery profile. So again, going back to what we saw here, we have these different sort of delivery profile that we created. And I can just go and open this first link so the image appears as it is. But now let's say if I go and open this crop square 300 link, you will see that it's a 300 by 300 image. So basically using the delivery profile, you are defining the transformation that is required. And that's what gets applied to the image that you're uploading. And there are a few scenarios where you might want to use your existing image structure itself and not modify a lot of things on the front end. Even that's possible with Media Optimizer will not get into the depths of it. But the idea is Media Optimizer allows you to create some sort of a mapping function where we can take your existing URLs. And we can translate it into Cloudinary Specific Transformation. But again, the key idea behind Media Optimizer is in scenarios where you do not want the image optimization logic to be in your front end, but is handled by a platform like the Cloudinary Media Optimizer platform. Yeah, so again, we saw two techniques over here when it comes to quick media optimization with Cloudinary. One is where we treat media as code and two is by leveraging Media Optimizer where you define rules at the platform level. And one more key thing which I want to mention is both Media as Code as well as Media Optimizer, the assets that you want to deliver to your end users are getting delivered using multi CDN. So we do use industry leading CDNs. And it's not just one CDN that we use. We use multi. This means based on your end user's location. In real time, dynamically, we decide that, hey, this CDN is the best for this particular end user's location and network. And this is where this is the CDN to be used to serve traffic for that user. So that's how we leverage multi CDN to deliver all these assets as part of Media as Code as well as Media Optimizer. So, yeah, just to conclude, right, as we saw from Marissa's talk, media optimization does matter when it comes to visual performance, user engagement with your site, as well as definitely for the Google search score. And in order to improve media performance, you definitely need to measure. You can only improve things that can be measured. Right. So performance measurement needs to be part of your development effort, similar to what you have been doing for QA load or stress testing. And then definitely optimize. Right. As we saw from Akshay's demo, focusing on three key pillars, quality, format, size, you were able to get quick media optimization. So when it comes to core web vitals, as we saw earlier, LCP, CLS and FID. So with focusing on these three key pillars, you will be able to definitely optimize the LCP very quickly. And also you saw the two techniques that can be used when it comes to Cloudinary Media as Code as well as the Media Optimizer. And media optimization is definitely a vast topic, although we did focus on three key pillars that you could quickly optimize to get some performance improvement. There are other key areas that you can definitely explore that we have listed over here. And you can also try Cloudinary for free. You will be provided with enough credits to get started along with access to our SDKs add-ons as well as our support team. And also if you think you need more training on the platform or maybe like SDKs or development related stuff, there are live or on-demand focused training that you can attend. Some references that we have added. Yeah, I think that's that's all we had for today. Hope you enjoyed the session. And if you have any questions, you can definitely post it over here. Thank you. And for those of you who are still around, I did want to just quickly mention regarding the developer courses, we actually have the intro for developer courses next week. So if any of you are interested in Cloudinary, if you haven't heard of us yet and just kind of interested in what we have and what we offer. Actually, I and another colleague will be teaching the intro for developers course next week in the Americas. And we also have it in European time as well. And they are both next week. So feel free to sign up for those courses as well if you want to learn more about Cloudinary. And we'll be sharing the presentation as well. And it has our LinkedIn so you can just come in and think as if you need any help as well. It looks like a few of you are asking about where you can sign up for the course, so I'll go ahead and put the link also in the discord channel so you guys will have that and in this chat as well. Thank you. Thank you. Thank you. Thank you. Thank you. Okay, so I think there is a question from Alexei on image optimization and retina display. So yes, so the way optimization works with retina display is, I think Pramod did mention this, there is something called device pixel ratio. This is something that your browser actually tells or is able to compute. Now, if there is a way you can capture that and then use it for displaying images, that is how retina basically works. Now, in the case of Cloudinary, there are multiple ways. So you can actually use the picture element and say like, hey, here is a small version, a double pixel density version and so on. Or you can just use a javascript solution we have, which I think Pramod mentioned as W-Auto. What it does is at the time of making the request for the URL, it replaces the width and DPR-Auto, which is the device pixel ratio. So it picks the width and then picks the device pixel ratio and sends that out so that we can dynamically create that image for you. So that's that way you actually get the image that is necessary for that kind of a device. The problem with DPR is we are seeing some DPR ratios that don't really make sense. If you make it completely dynamic, we see all kinds of values from one to actually I think almost up to three and a half. And they will be like all weird numbers like 3.49, 3.325 and things like that. So a better mechanism if you really want to go down that route is to actually constrain it and say we will always give you say DPR 1.5 kind of midway between regular devices and retina. Or we'll give you in three different variations. We'll give you like 1, 2 and 2.5. Honestly, anything beyond that, a human eye will not even notice. So it may not even make sense to go beyond that DPR at this point in time for the kind of devices or displays we have. Hope that helps. Since we have a few more minutes. So one other thing that I guess would be of interest is the whole image world is actually kind of rethinking all the things that we have about images and image formats. So there is a new format that is going to come out. It's called JPEG Excel. I think the Chrome will be one of the earlier browser that supports it. And it is actually going to kind of incorporate all the things that are possible with all the different image formats. And it's going to be like the one image format that will kind of at least with its spec. It's one image format for every purpose. So let's see how that shapes up. So that is something that is going to come out. OK, so the question again from Alexei is what happens with Retina display and then how do you make sure that it stays sharp when you do a pinch and zoom? So that is I think it's a very use case specific thing. So we may need to figure out what is going on. So say, for example, if you're using it for some kind of like an image display in the sense of like, here's a photo I took and I'm sharing it with my friends or Facebook, Instagram, kind of a thing. Then you may want to not actually bring it down in size. You may want to show like a full res image only when a user initiates certain action. So you may want to bring down like a quality auto, but then keep everything else same. Or it could be that if the use case is more like you have an e-commerce front or a blog post where images just being used to garnish the page, you don't really want user to even pinch and zoom. In that case, that specific problem doesn't even arise. So it's kind of dependent on what you're trying to achieve there. All right. I think we'll probably start wrapping up soon if there isn't any other questions. Waiting for any other questions. Going once. Going twice. Okay. So I see there's a question about any promo codes going on. So not at the moment. I would highly suggest that you actually start on the free plan with Cloudinary just so you can kind of make sure this is, you know, the product for you. And you can really try out all of the different image and video transformations and start from there. And then I know that if you want to sign up for an annual plan, then you'll get 10 percent off of your plan. If you even go outside of the free plans credits that we have, it's a pretty generous free plan, I'd say, to at least start out with. So if you don't have a large e-commerce site or whatever it may be that you're working on, you might find out that you can actually just continue using Cloudinary for free. Cool. I think there are two other questions so we can actually answer live. So one of them is, do we store the images or just sell them? So we have only spoken on one aspect of what we offer, which is just the image transformation. We do have a whole digital asset management solution. So that pretty much is all about storage, tagging, metadata, search and so on. So that's definitely available. Even if you don't sign up, even with regular images, yes, we do store them. And the typical model we try to suggest to customers is when you're using Cloudinary, try to treat us as a single source of truth for all your images. Not only do we save the image, but every transform that we create, we also store them. So that is the most complex operation. So that happens exactly once. And while delivering, we actually deliver it over our multi-CDN hostnames. So they get cached on the CDN as well. So there are multiple layers of storage, if I may say so, the actual storage of the image, the original, then the storage of the derivatives and caching of all the things that are being delivered. There's one more question. Can we use the free version commercially? Yes, you can. I think Marissa is also typing the answer there. Go ahead. But yeah, so just a quick alert. Yes, you can. What will happen is as long as you don't hit the free tier plan limit, it'll just work fine. And once you cross that, we'll start asking you to upgrade so that you can continue on delivering and there's no breakage of service. So yes, you can. Cool. I think we are on time. Yes, we're right at time. Thank you, everyone. Great. Thanks, everybody. Oh, I think there's one last question. Do you count every image is served, even if it is cached on the CDN? Okay, so the question is around the hits and bandwidth. So the bandwidth gets counted. So we don't really care about the hits per se on the CDN. But for delivery, there is bandwidth being consumed. So even if the image is being cached, the CDN does charge for the bandwidth. So that gets passed on as part of the overall billing. Hey, no problem. Okay, thank you. All right. Hope to see some of you guys at the course next week. Thank you. Have a good one. Bye, guys.