{"id":29524,"date":"2023-06-27T07:00:00","date_gmt":"2023-06-27T14:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=29524"},"modified":"2023-10-13T09:06:01","modified_gmt":"2023-10-13T16:06:01","slug":"checking-network-strength-load-better-images-cloudinary","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary","title":{"rendered":"Checking Network Strength to Progressively Load Better Images With Cloudinary"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>In my <a href=\"https:\/\/cloudinary.com\/blog\/automatically-loading-high-quality-images-cloudinary-intersectionobserver\">last article<\/a>, I discussed how you could use the IntersectionObserver API to progressively load a higher-quality version of an image when it was in the viewport. This was, in my opinion, a great use of web standards to provide a better experience where possible, while still providing a good experience by default. While that\u2019s a basic tenant of progressive enhancement, I think it\u2019s even better when combined with Cloudinary APIs to handle the heavy lifting of creating different quality versions of your media. While researching that article, I came across <em>another<\/em> option I\u2019d like to explore today: the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Network_Information_API\">Network Information API<\/a>.<\/p>\n<h2>The Network Information API<\/h2>\n<p>The Network Information API provides information about \u2014 wait for it \u2014 the user\u2019s connection to the internet. As an API, it\u2019s various properties you can check and an event listener that can fire on <em>changes<\/em> to their connection.<\/p>\n<p>The API is <em>mostly<\/em> supported in Chromium browsers (Chrome and Edge), but not supported in Safari. If you want to see more about possible support in Safari, you can check out this <a href=\"https:\/\/bugs.webkit.org\/show_bug.cgi?id=185697\">seven-year-old bug report<\/a> and cross your fingers. (As an aside, for a while now Safari has basically been the \u201cNew IE,\u201d at least in my opinion. I want to give Apple credit, though, as over the last year, they\u2019ve made some great strides in getting Safari updated with more features.) I say \u201cmostly\u201d as, while you can check some things in Chromium browsers, you can\u2019t check everything according to the spec. For our demo today, we\u2019ll be fine, but it\u2019s something to keep in mind. For further details, I\u2019d check the <a href=\"https:\/\/caniuse.com\/netinfo\">CanIUse<\/a> page for the API as well as <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Network_Information_API#browser_compatibility\">MDN\u2019s compatibility table<\/a><\/p>\n<p>With that in mind, let\u2019s look at the properties you can check. All of the below can be found in the <code>navigator.connection<\/code> object.<\/p>\n<ul>\n<li>\n<strong>downlink and downlinkMax<\/strong>. These two properties return the effective and max estimated bandwidth for downloads.<\/li>\n<li>\n<strong>effectiveType<\/strong>. Gives a label to the estimated network connection. Returns one of <code>slow-2g<\/code>, <code>2g<\/code>, <code>3g<\/code>, or <code>4g<\/code>.<\/li>\n<li>\n<strong>rtt<\/strong>. Returns an estimate of round-trip time for the connection. This represents the duration, in milliseconds, from when a browser sends a request to a <a href=\"https:\/\/cloudinary.com\/glossary\/edge-server\">server<\/a> and gets a response<\/li>\n<li>\n<strong>saveData<\/strong>. Returns true if the device making the connection is using a \u2018reduced data\u2019 setting.<\/li>\n<li>\n<strong>type<\/strong>. Returns a more broad category of connection. For example, <code>ethernet<\/code>, <code>bluetooth<\/code>, and more, but this is <em>barely<\/em> supported so don\u2019t get to excited.<\/li>\n<\/ul>\n<p>And as mentioned above, you can also listen for a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/NetworkInformation\/change_event\">change<\/a> event to handle a user\u2019s connection switching from one type to another.<\/p>\n<p>While this is probably assumed, be aware that these are all \u201cread-only\u201d properties. You can\u2019t magically upgrade the speed of a user\u2019s connection.<\/p>\n<p>If we assume only Chromium, we can only safely use <code>downlink<\/code>, <code>effectiveType<\/code>, and <code>rtt<\/code>. Here\u2019s an example of what this is returning for me, right now, in an office with Wi-Fi:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json shcb-wrap-lines\">{\n\t<span class=\"hljs-attr\">\"downlink\"<\/span>: <span class=\"hljs-number\">10<\/span>, \n\t<span class=\"hljs-attr\">\"effectiveType\"<\/span>: <span class=\"hljs-string\">\"4g\"<\/span>,\n\t<span class=\"hljs-attr\">\"rtt\"<\/span>: <span class=\"hljs-number\">100<\/span>,\n\t<span class=\"hljs-attr\">\"saveData\"<\/span>: <span class=\"hljs-literal\">false<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>As I said, I\u2019m currently connected to Wi-Fi, but according to the spec, the highest value possible with <code>effectiveType<\/code> is \u201c4g\u201d. With that in mind, we can use a check for \u201c4g\u201d as a way of recognizing a user with great connection. Let\u2019s take a look at a demo utilizing this.<\/p>\n<h2>Building an Unsplash Search Tool<\/h2>\n<p>For our demo, we\u2019ll begin with a simple example of the <a href=\"https:\/\/unsplash.com\/developers\">Unsplash API<\/a>. The Unsplash API is a simple way to search their intensive catalog of media and render those results on your site. For this first iteration, our demo will simply provide a form field for searching, a button to perform the search, and then render the results.<\/p>\n<p>To make things a bit easier, and to help keep this demo live within the constraints of Unsplash\u2019s API limits, the form will both default to <code>cats<\/code> for a search and if that keyword is used, it will store results in your LocalStorage. Obviously, you wouldn\u2019t do this in production and you should feel free to change the search term, but if you do, please consider getting your own developer key.<\/p>\n<p>Finally, I also made use of the excellent <a href=\"https:\/\/masonry.desandro.com\/\">Masonry<\/a> library by <a href=\"https:\/\/desandro.com\/\">David DeSandro<\/a>.<\/p>\n<p>Alright, let\u2019s first look at the HTML:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"search\"<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"search\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"cats\"<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"searchBtn\"<\/span>&gt;<\/span>Search<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"results\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"grid\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>As you can see, it\u2019s pretty short. But here\u2019s where the magic happens in the JavaScript:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> UNSPLASH_KEY = <span class=\"hljs-string\">'N5Kzqu4gnAFeJGIXYLGxRY5ejv8ryh1PvT32XaPftMM'<\/span>;\n\n<span class=\"hljs-comment\">\/*\nIn order to keep myself (and others) under Unsplash's limits, I'll cache the result of cats. \n*\/<\/span>\n<span class=\"hljs-keyword\">const<\/span> CACHING = <span class=\"hljs-literal\">true<\/span>;\n\n<span class=\"hljs-keyword\">let<\/span> $search = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'#search'<\/span>);\n<span class=\"hljs-keyword\">let<\/span> $results = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'#results'<\/span>);\n<span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'#searchBtn'<\/span>).addEventListener(<span class=\"hljs-string\">'click'<\/span>, doSearch, <span class=\"hljs-literal\">false<\/span>);\n\n<span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">doSearch<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\t<span class=\"hljs-keyword\">let<\/span> term = $search.value.trim();\n\t<span class=\"hljs-keyword\">if<\/span>(term === <span class=\"hljs-string\">''<\/span>) <span class=\"hljs-keyword\">return<\/span>;\n\t<span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`search for <span class=\"hljs-subst\">${term}<\/span>`<\/span>);\n\t<span class=\"hljs-keyword\">let<\/span> data;\n\t<span class=\"hljs-comment\">\/\/ Logic for caching<\/span>\n\t<span class=\"hljs-keyword\">if<\/span>(CACHING &amp;&amp; term === <span class=\"hljs-string\">'cats'<\/span> &amp;&amp; localStorage&#91;<span class=\"hljs-string\">'us_cats'<\/span>]) {\n\t\t<span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Returning cache'<\/span>);\n\t\tdata = <span class=\"hljs-built_in\">JSON<\/span>.parse(localStorage&#91;<span class=\"hljs-string\">'us_cats'<\/span>]);\n\t} <span class=\"hljs-keyword\">else<\/span> {\n\t\t<span class=\"hljs-keyword\">let<\/span> res = <span class=\"hljs-keyword\">await<\/span> fetch(<span class=\"hljs-string\">`https:\/\/api.unsplash.com\/search\/photos?page=1&amp;query=<span class=\"hljs-subst\">${<span class=\"hljs-built_in\">encodeURIComponent<\/span>(term)}<\/span>&amp;client_id=<span class=\"hljs-subst\">${UNSPLASH_KEY}<\/span>&amp;per_page=30`<\/span>);\n\t\tdata = <span class=\"hljs-keyword\">await<\/span> res.json();\n\t}\n\t<span class=\"hljs-comment\">\/\/console.log(data);<\/span>\n\t<span class=\"hljs-keyword\">if<\/span>(data.total === <span class=\"hljs-number\">0<\/span>) <span class=\"hljs-keyword\">return<\/span>;\n\t<span class=\"hljs-keyword\">let<\/span> results = <span class=\"hljs-string\">''<\/span>;\n\tdata.results.forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">d<\/span> =&gt;<\/span> {\n\t\tresults += <span class=\"hljs-string\">`&lt;div class=\"grid-item\"&gt;&lt;img src=\"<span class=\"hljs-subst\">${d.urls.small}<\/span>\"&gt;&lt;\/div&gt;`<\/span>;\n\t});\n\t$results.innerHTML = results;\n\n\t<span class=\"hljs-keyword\">let<\/span> msnry = <span class=\"hljs-keyword\">new<\/span> Masonry($results,{\n\t\t\t<span class=\"hljs-attr\">itemSelector<\/span>: <span class=\"hljs-string\">'.grid-item'<\/span>,\n\t\t\t<span class=\"hljs-attr\">columnWidth<\/span>: <span class=\"hljs-string\">'.grid-item'<\/span>\n\t});\n\timagesLoaded( $results ).on( <span class=\"hljs-string\">'progress'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\t\tmsnry.layout();\n\t});\n\t\n\t\n\t<span class=\"hljs-keyword\">if<\/span>(CACHING &amp;&amp; term === <span class=\"hljs-string\">'cats'<\/span>) {\n\t\t<span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'saving cache'<\/span>);\n\t\tlocalStorage&#91;<span class=\"hljs-string\">'us_cats'<\/span>] = <span class=\"hljs-built_in\">JSON<\/span>.stringify(data);\n\t}\n\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>From the top, we begin by caching a few DOM selectors and adding an event listener to our button. When a search is performed, we alternate between checking for a cached version (Again, only if you searched for <code>cats<\/code>, and honestly, why search for anything else?) and hitting the Unsplash API.<\/p>\n<p>Once we have our results, we iterate over them and make use of the <code>small<\/code> version of each result. Their API returns multiple different sizes but for our purposes, the <code>small<\/code> URL is fine.<\/p>\n<p>We then render this out and use the Masonry library to make it pretty. Try it yourself:<\/p>\n<p class=\"codepen\" data-height=\"700\" data-default-tab=\"result\" data-slug-hash=\"KKGqqew\" data-user=\"cfjedimaster\" style=\"height: 700px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;\">\n  <span>See the Pen <a href=\"https:\/\/codepen.io\/cfjedimaster\/pen\/KKGqqew\">\n  Unsplash<\/a> by Raymond Camden (<a href=\"https:\/\/codepen.io\/cfjedimaster\">@cfjedimaster<\/a>)\n  on <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<\/span>\n<\/p>\n<script async src=\"https:\/\/cpwebassets.codepen.io\/assets\/embed\/ei.js\"><\/script>\n<h2>Enhancing Images With Cloudinary and the Network Information API<\/h2>\n<p>For our Cloudinary version, we\u2019ll make use of two simple transformations. First, we\u2019ll use <a href=\"https:\/\/cloudinary.com\/documentation\/fetch_remote_images\">Fetch<\/a> to enable Cloudinary to use the remote asset, which will give us access to Cloudinary\u2019s CDN and <a href=\"https:\/\/cloudinary.com\/glossary\/cdn-caching\">caching<\/a> as well.<\/p>\n<p>Next, we\u2019ll use Cloudinary\u2019s <a href=\"https:\/\/cloudinary.com\/documentation\/image_optimization#set_the_quality_when_delivering_an_image\">quality transformation<\/a> to return a lower-quality image by default, and a higher-quality one when the Network Information API suggests it\u2019s acceptable.<\/p>\n<p>The changes for this start in our search handler where instead of returning the URL, I wrap it in a call to a new function:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">data.results.forEach(d =&gt; {\n\tresults += `<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"grid-item\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"${getOptimizedImage(d.urls.small)}\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>`;\n});\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>If it\u2019s hard to see in there, the new function call is <code>getOptimizedImage<\/code>, and we pass the URL that Unsplashed returned. Now let\u2019s look at that new function:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getOptimizedImage<\/span>(<span class=\"hljs-params\">u<\/span>) <\/span>{\n\t<span class=\"hljs-keyword\">if<\/span>(canDoHighRes()) {\n\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/raymondcamden\/image\/fetch\/q_100\/<span class=\"hljs-subst\">${<span class=\"hljs-built_in\">encodeURIComponent<\/span>(u)}<\/span>`<\/span>;\t\n\t} <span class=\"hljs-keyword\">else<\/span> {\n\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/raymondcamden\/image\/fetch\/q_30\/<span class=\"hljs-subst\">${<span class=\"hljs-built_in\">encodeURIComponent<\/span>(u)}<\/span>`<\/span>;\t\n\t}\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This function makes use of another new function (you\u2019ll see in a second) to determine if a higher resolution image makes sense. If it does, it returns the image at 100% quality, and if not, down to 30%. Now let\u2019s look at the final function:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">canDoHighRes<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\t<span class=\"hljs-keyword\">if<\/span>(navigator?.connection?.effectiveType === <span class=\"hljs-string\">'4g'<\/span>) <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">true<\/span>;\n\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">false<\/span>;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This simply checks <code>effectiveType<\/code> and if it\u2019s the highest possible value, returns true. You can try out this version below:<\/p>\n<p class=\"codepen\" data-height=\"700\" data-default-tab=\"result\" data-slug-hash=\"wvYrKJm\" data-user=\"cfjedimaster\" style=\"height: 700px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;\">\n  <span>See the Pen <a href=\"https:\/\/codepen.io\/cfjedimaster\/pen\/wvYrKJm\">\n  Unsplash + Cloudinary<\/a> by Raymond Camden (<a href=\"https:\/\/codepen.io\/cfjedimaster\">@cfjedimaster<\/a>)\n  on <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<\/span>\n<\/p>\n<script async src=\"https:\/\/cpwebassets.codepen.io\/assets\/embed\/ei.js\"><\/script>\n<p>How does this work when tested? I opened up the demo in Firefox, where the Network Information API\u2019s <code>effectiveType<\/code> value won\u2019t be available. Even at lower quality, the results are still great.<\/p>\n<\/div>\n\n\n<figure class=\"wp-block-image size-full\"><img width=\"800\" height=\"425\" data-public-id=\"Web_Assets\/blog\/firefox\/firefox.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_800,h_425,c_scale\/f_auto,q_auto\/v1686595286\/Web_Assets\/blog\/firefox\/firefox.png?_i=AA\" alt=\"\" class=\"wp-post-29524 wp-image-29525\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1686595286\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1686595286\/Web_Assets\/blog\/firefox\/firefox.png?_i=AA 800w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1686595286\/Web_Assets\/blog\/firefox\/firefox.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1686595286\/Web_Assets\/blog\/firefox\/firefox.png?_i=AA 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Firefox&#8217;s render<\/figcaption><\/figure>\n\n\n<div class=\"wp-block-cloudinary-markdown \"><p>In Firefox\u2019s devtools, it reported 29 images at a total of 243K. Pretty good for so many images.<\/p>\n<p>Running the same test in Chrome shows pretty much the same thing, but much crisper:<\/p>\n<\/div>\n\n\n<figure class=\"wp-block-image size-full\"><img width=\"800\" height=\"426\" data-public-id=\"Web_Assets\/blog\/chrome\/chrome.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_800,h_426,c_scale\/f_auto,q_auto\/v1686595283\/Web_Assets\/blog\/chrome\/chrome.png?_i=AA\" alt=\"\" class=\"wp-post-29524 wp-image-29526\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1686595283\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1686595283\/Web_Assets\/blog\/chrome\/chrome.png?_i=AA 800w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1686595283\/Web_Assets\/blog\/chrome\/chrome.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1686595283\/Web_Assets\/blog\/chrome\/chrome.png?_i=AA 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Chrome&#8217;s result<\/figcaption><\/figure>\n\n\n<div class=\"wp-block-cloudinary-markdown \"><p>The screenshot probably doesn\u2019t do it justice, but it does look better. Of course, that\u2019s because the higher-resolution images were loaded. In devtools, the size now is 875kb.<\/p>\n<p>As a reminder, all modern devtools now support throttling in the network panel. In Chrome, I switched to \u201cSlow 3G\u201d for a throttle, ran the search again, and this time the lower-res images were used. (As a developer pro-tip, don\u2019t forget to stop throttling when you\u2019re done.)<\/p>\n<h2>Wrap Up<\/h2>\n<p>So unlike the previous article which made use of the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Intersection_Observer_API\">IntersectionObserver API<\/a>, the Network Information API is <em>much<\/em> less supported and not quite as useful. Using it to load much higher (or even larger) images could be useful, but just keep in mind the level of support before making use of it in your own projects.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":87,"featured_media":29646,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[332,134,370,165],"class_list":["post-29524","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-api","tag-guest-post","tag-image","tag-image-transformation"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Checking Network Strength to Load Better Images With Cloudinary<\/title>\n<meta name=\"description\" content=\"In this article, we discuss how to use Cloudinary&#039;s quality transformation and the Network API to enhance your images.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Checking Network Strength to Progressively Load Better Images With Cloudinary\" \/>\n<meta property=\"og:description\" content=\"In this article, we discuss how to use Cloudinary&#039;s quality transformation and the Network API to enhance your images.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-06-27T14:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-10-13T16:06:01+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1687330137\/Web_Assets\/blog\/Blog-network-strength\/Blog-network-strength-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"melindapham\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"Checking Network Strength to Progressively Load Better Images With Cloudinary\",\"datePublished\":\"2023-06-27T14:00:00+00:00\",\"dateModified\":\"2023-10-13T16:06:01+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary\"},\"wordCount\":16,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1687330137\/Web_Assets\/blog\/Blog-network-strength\/Blog-network-strength.jpg?_i=AA\",\"keywords\":[\"API\",\"Guest Post\",\"Image\",\"Image Transformation\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2023\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary\",\"url\":\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary\",\"name\":\"Checking Network Strength to Load Better Images With Cloudinary\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1687330137\/Web_Assets\/blog\/Blog-network-strength\/Blog-network-strength.jpg?_i=AA\",\"datePublished\":\"2023-06-27T14:00:00+00:00\",\"dateModified\":\"2023-10-13T16:06:01+00:00\",\"description\":\"In this article, we discuss how to use Cloudinary's quality transformation and the Network API to enhance your images.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1687330137\/Web_Assets\/blog\/Blog-network-strength\/Blog-network-strength.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1687330137\/Web_Assets\/blog\/Blog-network-strength\/Blog-network-strength.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Checking Network Strength to Progressively Load Better Images With Cloudinary\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\",\"name\":\"melindapham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"caption\":\"melindapham\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Checking Network Strength to Load Better Images With Cloudinary","description":"In this article, we discuss how to use Cloudinary's quality transformation and the Network API to enhance your images.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary","og_locale":"en_US","og_type":"article","og_title":"Checking Network Strength to Progressively Load Better Images With Cloudinary","og_description":"In this article, we discuss how to use Cloudinary's quality transformation and the Network API to enhance your images.","og_url":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary","og_site_name":"Cloudinary Blog","article_published_time":"2023-06-27T14:00:00+00:00","article_modified_time":"2023-10-13T16:06:01+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1687330137\/Web_Assets\/blog\/Blog-network-strength\/Blog-network-strength-jpg?_i=AA","type":"image\/jpeg"}],"author":"melindapham","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"Checking Network Strength to Progressively Load Better Images With Cloudinary","datePublished":"2023-06-27T14:00:00+00:00","dateModified":"2023-10-13T16:06:01+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary"},"wordCount":16,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1687330137\/Web_Assets\/blog\/Blog-network-strength\/Blog-network-strength.jpg?_i=AA","keywords":["API","Guest Post","Image","Image Transformation"],"inLanguage":"en-US","copyrightYear":"2023","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary","url":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary","name":"Checking Network Strength to Load Better Images With Cloudinary","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1687330137\/Web_Assets\/blog\/Blog-network-strength\/Blog-network-strength.jpg?_i=AA","datePublished":"2023-06-27T14:00:00+00:00","dateModified":"2023-10-13T16:06:01+00:00","description":"In this article, we discuss how to use Cloudinary's quality transformation and the Network API to enhance your images.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1687330137\/Web_Assets\/blog\/Blog-network-strength\/Blog-network-strength.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1687330137\/Web_Assets\/blog\/Blog-network-strength\/Blog-network-strength.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/checking-network-strength-load-better-images-cloudinary#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Checking Network Strength to Progressively Load Better Images With Cloudinary"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9","name":"melindapham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","caption":"melindapham"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1687330137\/Web_Assets\/blog\/Blog-network-strength\/Blog-network-strength.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/29524","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=29524"}],"version-history":[{"count":11,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/29524\/revisions"}],"predecessor-version":[{"id":31462,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/29524\/revisions\/31462"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/29646"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=29524"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=29524"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=29524"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}