Gregory's Blog

Demonstrating the benefits of the new webp image format

The next generation webp format is one of the most important image formats to have come along in the last several years.

My first impression of webp images was 'meh'. I read articles on the next generation webp image format. The articles claim that webp images are about 26% smaller than their png equivalents. What I read was impressive, but not just enough to delve into learning how to use them.

I only started playing around with webp to chase a better Google lighthouse score. Google knocked my 'Best Practices' score down a couple of notches, and I finally decided to investigate using them. I'm glad that I did! I did not find a 26% improvement with my own images, but a 95% improvement in size than the identical .png images that I had created. Want proof? I'll demonstrate my own results...

Please take a look at my Greensock animation introducing the next version of Galaxie Blog at https://gregoryalexander.com/blog/index.cfm/2019/9/16/Galaxie-Blog-135-Animated-Blurb. If you're using a modern browser, you should notice that this intense graphical scene renders rather quickly. On a vain note- I also hope that you find the scollmagic scene to be pretty...

The scrollmagic scene that you are looking at should deliver images in the webp format if your browser supports them. If the browser does not support webp, png's will be rendered instead. I'll go over the code later on, but right now I will compare the image sizes to show you the improvements. I'll provide the link and size of each actual image in the grid below so you can take a look yourself.

I created the original source images using Adobe Illustrator, and exported them to png's using the 'save as legacy web' option. To create the web images, I took the png images that I had created and used Irfanview to export the png as a webp. Take a look at the size reduction in the table below.

The entire payload of all of the combined webp images is 90% less than one of the actual png images! With this type of extreme size reduction we can vastly improve our loading time, or we can now use full quality images in animated sequences like this!

I am also using webp instead of jpg images. Here the improvements are not as as substantial, but I have shaved about 10-15% of the size of the jpg images.

How to get started with webp images.

Server side code

Currently, with the exception of Safari- most modern HTML5 capable browsers support webp images. To determine if a browser supports the next generation format is relatively easy. Thankfully, nearly all modern browsers will include webp support in the header if the browser supports the webp next gen image. Here is the ColdFusion script that I developed.

view plain about
1<!--- Determine if the http accept header contains webp. The getHttpRequestData().headers is a structure and we are targetting the accept element in the array. Note: nearly all modern browsers will include this if the browser supports the webp next gen image. --->
2<cfset acceptHeader = getHttpRequestData().headers["accept"]>
3<!--- Does the header accept webp? --->
4<cfif findNoCase("webp", acceptHeader) gt 0>
5    <cfset clientAcceptsWebP = true>
6<cfelse>
7    <cfset clientAcceptsWebP = false>
8</cfif>

A hybrid server side and client side approach

Most web servers support the webp image format as well. However, on web servers that are not current, you may need to add the webp mime type. Consult with your ISP or server documentation to find out how to add webp mime type on your server. Here is the function that I developed to test webp support on the server and save the result as a persistent application variable. You will have to create a small webp image and upload to your own server for this to work (see headerBodyDivider.webp below).

view plain about
1<!--- Determine if the webP mime type is set up on the server. --->
2    <cffunction name="serverSupportsWebP" access="public" returntype="boolean" output="yes">
3        <!--- Note: we need to eliminate https from the root URL if it exists. I ran into errors trying this with https (a cryptic certificate error). --->
4        <cfset thisUrl = replaceNoCase(application.rootUrl, "https", "http")>
5        <!--- The headerBodyDivider image is a tiny .webp image (around 1k). We are going to read this, and if it was found and the mime type is correct, we will assumed that the mime type is correct. Otherwise, we will determine that the server does not support the webp mime type. --->
6        <cfhttp method="get" URL="#thisUrl#/images/divider/headerBodyDivider.webp">
7            
8        <!--- Was the webp image found? --->
9        <cfif cfhttp.mimeType contains 'webp'>
10            <cfset webp = true>
11        <cfelse>
12            <cfset webp = false>
13        </cfif>
14        <!--- Return it. --->
15        <cfreturn webp>
16    </cffunction>

In my code, I either use the server side logic above to test both the server and the client for webp support, or I use a hybrid approach where I save the result of the serverSupportsWebP server side function and test for support using javascript on the client like so:

view plain about
1<script>
2        // WebP support detection. Revised a script found on stack overflow: https://stackoverflow.com/questions/5573096/detecting-webp-support. It is the quickest loading script to determine webP that I have found so far.
3        function webPImageSupport() {
4            // Detemine if the webp mime type is on the server. This is saved as a ColdFusion application variable.
5            var serverSupportsWebP = <cfoutput>#application.serverSupportsWebP#</cfoutput>;
6         var elem = document.createElement('canvas');
7            
8         if (serverSupportsWebP && !!(elem.getContext && elem.getContext('2d'))) {
9         // Is able to get WebP representation?
10         return elem.toDataURL('image/webp').indexOf('data:image/webp') == 0;
11         }
12         // webp is not supported on older browsers such as IE.
13         return false;
14        }
15    </script>

Using either approach is valid. However, I typically use server side code to set the paths of my images, and only use the hybrid approach (using the script above) when I am manipulating the DOM with javascript.

Example server side logic to set the image extension

I personally set the default image to be png or jpg, and only change it if both the client and the server support webp:

view plain about
1<!--- Determine if the http accept header contains webp. The getHttpRequestData().headers is a structure and we are targetting the accept element in the array. Note: nearly all modern browsers will include this if the browser supports the webp next gen image. --->
2    <cfset acceptHeader = getHttpRequestData().headers["accept"]>
3    <!--- Does the header accept webp? --->
4    <cfif findNoCase("webp", acceptHeader) gt 0>
5        <cfset clientAcceptsWebP = true>
6    <cfelse>
7        <cfset clientAcceptsWebP = false>
8    </cfif>
9        
10    <!--- Logic to determine if the server has the necessary webp mime type was done in the application.cfc template. We will use the application.serverSupportsWebP variable that the mime type is installed on the server. Of course, both the client and the server need to support webp images before we can deliver them.--->
11    <cfif application.serverSupportsWebP and clientAcceptsWebP>
12        <cfset webpImageSupported = true>
13    <cfelse>
14        <cfset webpImageSupported = false>
15    </cfif>
16        
17    <cfif webpImageSupported>
18        <!--- Overwrite the headerBodyDividerImage var and change the extension to .webp --->
19        <cfset headerBodyDividerImage = replaceNoCase(headerBodyDividerImage, '.png', '.webp')>
20    </cfif>

Tips

When saving webp images, don't use the Adobe Photoshop or Illustrator plugin's. There are two of them as of today, and neither of them work well. Instead, just create your images and save them as jpg or png, and use Irfanview 64 to open and save them as a webp image. I have tried many approaches, and this method has proven to be the most reliable.

Thanks for reading, and happy coding!

Gregory

Comments

This entry was posted on September 17, 2019 at 12:31 AM and has received 305 views.

Introducing Galaxie Blog 1.35 with a new Parallax Scene


Galaxie Blog is a Next Generation Blog Platform

Galaxie Blog is the most beautiful and functional open sourced ColdFusion blog in the world.

galaxie Parallax Image 1
galaxie Parallax Image 2
galaxie Parallax Image 3
galaxie Parallax Image 4
galaxie Parallax Image 5
galaxie Parallax Image 6

Galaxie Blog is fast....

Galaxie Blog consistently performs above 80% in Google lighthouse scores...

For comparison, the industry average performance score of the top ecommerce sites is is around 22%

Galaxie Blog is Accessible...

Galaxie Blog has a perfect 100% 'Accessibility' Google Score

The industry average sites is around 60%.

Galaxie Blog has a perfect 100% 'Best Practices' Google Score

Galaxie Blog uses all of the 'Best Practices' suggested by Google.

The industry average score is about 61%

Galaxie Blog has a perfect 'Search Engine Opimization (SEO)' score.

Having a perfect SEO ensures that search engines will be able to collect and promote your content.

Galaxie Blog allows you to enforce SSL....

Blog owners can specify whether to enforce SSL. If SSL is enforced, all port 80 traffic will be a automatically redirected to an encryted port.

Galaxie Blog delivers next generation media content...

Galaxy Blog now delivers webp images if both the client and the server support the new webp format.

If webp is not supported, Galaxy Blog will fallback and deliver images using tradional web formats.

Improved Entry Image Support

Administrators can easilly add images to their entries by clicking on a button and selecting an image. Galaxie Blog will take care of the rest.

To improve performance, Galaxie Blog will only render the image once it comes into the user's viewport.

Improved Social Media Sharing

Galaxie Blog now prominently displays social media icons to share your social media at the bottom of each individual blog entry.

You can use dynamic includes within an entry using xml.

This very GSAP scene that you're looking at now is acheived using a dynamic cfinclude.

Goals for the next version...

Overhaul the original database and add 'Disqus' integration.

Galaxie Blog is still using the original BlogCfc database and it must be updated. If feasable, I will add Disqus comment integration.

 

Comments

This entry was posted on September 16, 2019 at 12:50 PM and has received 323 views.

Google Lighthouse Metrics for Galaxie Blog Version 1.35



I am finally done chasing a nearly perfect Google lighthouse score and have released Galaxie Blog version 1.35. I have a perfect score for 3 out of 4 categories, and the performance score generally falls between 70 and 90. Performance: 90%1 Accessibility: 100% Best Practices: 100%2 and SEO (Search Engine Optimization): 100%

Attaining this score was not easy- especially for a large blog site. This is major accomplishment.

I spent over a month working on all aspects of the site. The google audit checklist is exhaustive. For example, I am deferring all non essential resources until page load, lazy loading the images and media, using next generation image formats (webp)3 and networking protocols (http/2) if possible, have no errors written to the console, etc, etc.

I will write up several blog entries in the next couple of weeks to highlight some of my approaches.

1 Performance results can vary considerably.
2 Attained with the commercial Kendo license. The open source Kendo Best Practices score is at 86 due to the Kendo embedded jQuery library. According to Telerik, this should be fixed in the near future.
3 Webp images are only served when both the client and the server supports them. When webp is not supported, Galaxie Blog will fallback to traditional image protocols. I will write up an article on this in the near future.

Related Entries

Comments

This entry was posted on September 13, 2019 at 12:29 PM and has received 246 views.

How to speed up your site with lazy loading

Lazy loading is a process where you defer loading your non-essential scripts and media until after the page loads. With lazy loading, you don't load everything at once, however, you defer the loading of images and scripts until they are actually needed. This is essential if you're trying to improve the load time of your site.

In this technical article, I will show you the process that I used in order to vastly improve the performance of this blog site.

Let's take a second to visually see what lazy loading is by taking a look at my own site. If you navigate to my main blog at www.gregoryalexander.com/blog and slowly scroll down, you will notice that the images do not load until you reach them. When the image is in the viewport, you will notice an image that looks like it is fading in. I am not loading these images when the page loads, but am waiting for you to scroll down before loading them. In order to achieve this effect, we need to have a library that uses the intersection observer API .

This is how the 'big boy's', like Facebook do it. Follow along and I can walk you through in order to do it on your own.

There are a multitude of different lazy loading libraries out there. I have tried a handful of these libraries, but settled on defer.js by shinsenter. My requirements are more extensive than usual. Along with the my requirements to lazy load my blog media, I needed to defer the loading of Kendo UI and other extensive libraries such as the Green Sock Animation Platform (GSAP). These libraries need to have a strict order to which libraries are loaded that they depend upon.

I had some issues with a handful of other libraries, but defer.js allowed me to achieve what I need.

Kendo UI needs to have jQuery loaded prior to the extensive Kendo UI scripts, and Kendo UI requires loading a large javascript library along with common .css files, a .less based css file that is required by the users chosen theme, and mobile css for mobile clients. GSAP also has several dependencies. I'll go through the process that I used to properly defer these scripts until the page loads defer.js below. At the end of this technical article, I'll discuss how I lazy loaded the blog media as well.

Before we do anything, we need to insert the defer.js code in the head section of your document:

view plain about
1//* Script to defer script resources. See https://appseeds.net/defer.js/demo.html.
2// @shinsenter/defer.js */

3!function(e,o,t,n,i,r){function c(e,t){r?n(e,t||32):i.push(e,t)}function f(e,t,n,i){return t&&o.getElementById(t)||(i=o.createElement(e||'SCRIPT'),t&&(i.id=t),n&&(i.onload=n),o.head.appendChild(i)),i||{}}r=/p/.test(o.readyState),e.addEventListener('on'+t in e?t:'load',function(){for(r=t;i[0];)c(i.shift(),i.shift())}),c._=f,e.defer=c,e.deferscript=function(t,n,e,i){c(function(e){f(0,n,i).src=t},e)}}(this,document,'pageshow',setTimeout,[]),function(u,n){var a='IntersectionObserver',d='src',l='lazied',h='data-',p=h+l,y='load',m='forEach',r='appendChild',b='getAttribute',c=n.head,g=Function(),v=u.defer||g,f=v._||g;function I(e,t){return[].slice.call((t||n).querySelectorAll(e))}function e(s){return function(e,t,o,r,c,f){v(function(n,t){function i(n){!1!==(r||g).call(n,n)&&(I('SOURCE',n)[m](i),(f||['srcset',d,'style'])[m](function(e,t){(t=n[b](h+e))&&(n[e]=t)}),y in n&&n[y]()),n.className+=' '+(o||l)}t=a in u?(n=new u[a](function(e){e[m](function(e,t){e.isIntersecting&&(t=e.target)&&(n.unobserve(t),i(t))})},c)).observe.bind(n):i,I(e||s+'['+h+d+']:not(['+p+'])')[m](function(e){e[b](p)||(e.setAttribute(p,s),t(e))})},t)}}function t(){v(function(t,n,i,o){t=[].concat(I((i='script[type=deferjs]')+':not('+(o='[async]')+')'),I(i+o)),function e(){if(0!=t){for(o in n=f(),(i=t.shift()).parentNode.removeChild(i),i.removeAttribute('type'),i)'string'==typeof i[o]&&n[o]!=i[o]&&(n[o]=i[o]);n[d]&&!n.hasAttribute('async')?(n.onload=n.onerror=e,c[r](n)):(c[r](n),v(e,.1))}}()},4)}t(),u.deferstyle=function(t,n,e,i){v(function(e){(e=f('LINK',n,i)).rel='stylesheet',e.href=t},e)},u.deferimg=e('IMG'),u.deferiframe=e('IFRAME'),v.all=t}(this,document);

This script is short, and it can be either placed inline, or you can grab the code via CDN at https://github.com/shinsenter/defer.js/.

Defer Kendo UI.

Kendo UI requires loading the following files- in this order:
  1. jQuery (not deferred)
  2. kendoUiCore.js (or kendoUiAll if you're using your own license).
  3. The kendoUi common .css file.
  4. The kendoUi theme .css file.
  5. And the kendoUi mobile .css file

Here is how I achieved this in actual code. This code also needs to be placed in the head section of the page underneath the defer.js code:

view plain about
1// Load the javascript files.
2<script src="/common/libs/kendo/js/jquery.min.js"></script>
3<script type="deferjs" src="/common/libs/kendo/js/kendo.all.min.js"></script>
4// Load the style sheets
5<script type="deferjs">
6// Kendo common css. Note: Material black and office 365 themes require a different stylesheet. These are specified in the theme settings.
7$('head').append( $('&lt;link rel="stylesheet" type="text/css" />
').attr('href', '/common/libs/kendo/styles/kendo.common.min.css') );
8// Less based theme css files.
9$('head').append( $('&lt;link rel="stylesheet" type="text/css" />').attr('href', '/common/libs/kendo/styles/kendo.silver.min.css') );
10// Mobile less based theme file.
11$('head').append( $('&lt;link rel="stylesheet" type="text/css" />').attr('href', '/common/libs/kendo/styles/kendo.silver.mobile.min.css') );
12</script>

Note the deferjs in the script type. This is a keyword used by the defer.js library to defer these scripts until page load. Here, all of the Kendo UI resources are deferred other than the jQuery library.

Defer GSAP and ScrollMagic resources.

GSAP is an amazing library if you're interested in cutting edge animation effects. I use GSAP and ScrollMagic to provide for my parallax effects. See the entry Introducing Galaxie Blog for an example of a parallax effect.

My requirements to load GSAP and SrollMagic are:
  1. Load the tweenMax javascript library
  2. Load scrollMagic
  3. Load the main GSAP library
  4. Load the scrollToPlugin library
  5. And load my GSAP debugging script

All of the GSAP resources will be deferred until page load.
Here is the code:

view plain about
1<script type="deferjs" src="/blog/common/libs/greenSock/src/uncompressed/TweenMax.js"></script>
2<script type="deferjs" src="/blog/common/libs/scrollMagic/scrollmagic/uncompressed/ScrollMagic.js"></script>
3<script type="deferjs" src="/blog/common/libs/scrollMagic/scrollmagic/uncompressed/plugins/animation.gsap.js"></script>
4<script type="deferjs" src="/blog/common/libs/greenSock/src/uncompressed/plugins/ScrollToPlugin.js"></script>
5<script type="deferjs" src="/blog/common/libs/scrollMagic/scrollmagic/uncompressed/plugins/debug.addIndicators.js"></script>

Note: typically, the GSAP libraries are loaded at the end of the page, however, this did not work for me. I found that these files must be loaded in the head section.

Lazy loading media files.

My open source Galaxie Blog allows blog owners to upload pictures and media that show up on the top of every blog post. However, on the main blog page, this necessitates the client to download quite a lot of extra data. In order to achieve a decent page load time I had to figure out a way to delay the loading of this media. I also wanted to convey to the reader that something was happening and alert them with a subtle css effect when a new picture is loaded. Here is the defer.js approach:

CSS:

view plain about
1/* Lazy loading image classes */
2/* Initially hide the element with zero opacity */
3.fade {
4transition: opacity 500ms ease-in-out;
5opacity: 0;
6}
7
8/* Show it with the 'shown' class */
9.fade.shown {
10opacity: 1;
11background: 0 0;
12}


Javascript:
view plain about
1// Lazy loading images and media.
2// Callback function to add the 'shown' class into the element when it is loaded
3var media_loaded = function (media) {
4media.className += ' shown';
5}
6// Then call the deferimg and deferiframe methods
7deferimg('img.fade', 300, 'lazied', media_loaded);
8deferiframe('iframe.fade', 300, 'lazied', media_loaded);

Place the script and the CSS anywhere in your document as long as it is above the media that you're about to lazy load. Place the following javascript at the end of the page:

view plain about
1// Lazy load the images.
2deferimg('img.fade', 100, 'lazied', function(img) {
3img.onload = function() {
4img.className+=' shown';
5}
6});

Finally, include your image like so:

<img class="fade" data-src="https://gregoryalexander.com/blog/enclosures/8_31.png" alt="">

I'll quickly try to explain what is going on here. If you look carefully, you'll notice that the "src" tag is missing from the above image. The media does not show at first due to the src tag that is missing, and the opacity setting that is initially set to zero. When the defer.js library notices that the user has scrolled to the viewport where the image resides, defer.js takes the string found in the data-src tag and automatically builds the src dynamically. When the image is loaded to the client, the CSS briefly fades in.

You can use the same approach to defer and lazy load pretty much anything. If you're interested, you can dig into the meaty details by looking at my source code, or checking out the excellent defer.js demo.

Comments

This entry was posted on September 9, 2019 at 3:29 AM and has received 279 views.

Galaxie Blog status update



I have not been blogging for awhile now... and had wanted to wait to post anything until I got out another incremental version of the Blog before posting- but I am still working on it. Ben Nadel, Charlie Arehart, Wil Genovese, and other major bloggers have the ColdFusion blog world covered- and I like developing. I'm also a reluctant blogger. But, I digress- I wanted to write up a quick status update...

One of you out there (there has got to be one of you out there, right!?) may have noticed that I used the term 'Galaxie Blog', not 'Gregory's Blog', right? Why yes, that's right! I renamed it to Galaxie Blog! This blog has not had much attention, and it may be due to the perception that this is my own personal blog, which it's not. It's a major free open source ColdFusion project needing some love and attention!

I had multiple conversations with Charlie, and one of his suggestions was to change the name of this blog to 'Galaxie Blog'. And after some thought- I agree with him, thus the name change...

I have been working on a lot of things other than the easy branding change...

The new version, still in development, is 2-3x faster than this site. Google lighthouse is scoring the new development version between 70 and 90 percent on performance. The performance range is all over the place. I could get a 72 score, run it again, and get a 98 two seconds later (which is shown in the photo above). However, I got consistent results between 70 and 90. For comparison, the industry average performance score of the top ecommerce sites is is around 22% (https://www.practicalecommerce.com/70-leading-retailers-lighthouse-scores-revealed).

The Google lighthouse accessibility score is consistent at 100%. The average score of the top e-commerce sites is around 60%.

SEO (search engine optimization) score is still at 100%, but I made a deeper dive into current SEO practices and made several other improvements.

Best Practices is at 86%. The only major hits here were not using the http2 protocol (my ISP does not yet allow http2 calls yet, or the score would be much higher). Industry average is about 61%.

I am also quite close to being able to having Galaxie Blog (it feels weird writing that!) as a Progressive Web App. A Progressive Web App is a web application that is installable. It will still run when the user does not have an internet connection (via cache) and then send push notifications when the client is back online. However, I am still not sure if I will be able to finish this in this particular version.

I have a lot more stuff that I have made improvements upon, and I should have this new version out relatively soon... please bear with me.

Suggestions and comments would be quite welcomed!

Gregory

Lame PS Hello? Hello? Is anyone out there! Please feel free to say hello!

Comments

This entry was posted on September 1, 2019 at 1:00 AM and has received 372 views.

My biggest SEO problem....

Content... who in the hell is going to read anything about ColdFusion and Telerik Kendo integration anyway!? Sigh....

Comments

This entry was posted on September 1, 2019 at 12:50 AM and has received 288 views.




Footer Logo

Your input and contributions are welcomed!

If you have an idea, BlogCfc based code, or a theme that you have built using this site that you want to share, please contribute by making a post here or share it by contacting us! This community can only thrive if we continue to work together.

Images and Photography:

Gregory Alexander either owns the copyright, or has the rights to use, all images and photographs on the site. If an image is not part of the "Galaxie Blog" open sourced distribution package, and instead is part of a personal blog post or a comment, please contact us and the author of the post or comment to obtain permission if you would like to use a personal image or photograph found on this site.

Credits:

Portions of Galaxie Blog are powered on the server side by BlogCfc, an open source blog developed by Raymond Camden. Revitalizing BlogCfc was a part of my orginal inspiration that prompted me to design this site. Some of the major open source contributers to BlogCfc include:

  1. Peter Farrell: the author of 'Lyla Captcha' that is used on this blog.
  2. Pete Freitag: the author of the 'ColdFish' code formatter that is also used on this blog.

Version:

Galaxie Blog Version 1.45 October 9 2019














































xmlKeywords:descMetaTag getXmlKeywordValue(articles.body, 'facebookImageUrlMetaData'): /blog/includes/postContent/parallax/galaxieScript.cfm