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.

<!--- 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. --->
<cfset acceptHeader = getHttpRequestData().headers["accept"]>
<!--- Does the header accept webp? --->
<cfif findNoCase("webp", acceptHeader) gt 0>
	<cfset clientAcceptsWebP = true>
<cfelse>
	<cfset clientAcceptsWebP = false>
</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).

	<!--- Determine if the webP mime type is set up on the server. --->
	<cffunction name="serverSupportsWebP" access="public" returntype="boolean" output="yes">
		<!--- 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). --->
		<cfset thisUrl = replaceNoCase(application.rootUrl, "https", "http")>
		<!--- 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. --->
		<cfhttp method="get" URL="#thisUrl#/images/divider/headerBodyDivider.webp">
			
		<!--- Was the webp image found? --->
		<cfif cfhttp.mimeType contains 'webp'>
			<cfset webp = true>
		<cfelse>
			<cfset webp = false>
		</cfif>
		<!--- Return it. --->
		<cfreturn webp>
	</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:

<script>
		// 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.
		function webPImageSupport() {
			// Detemine if the webp mime type is on the server. This is saved as a ColdFusion application variable.
			var serverSupportsWebP = <cfoutput>#application.serverSupportsWebP#</cfoutput>;
    		var elem = document.createElement('canvas');
			
    		if (serverSupportsWebP && !!(elem.getContext && elem.getContext('2d'))) {
        		// Is able to get WebP representation?
        		return elem.toDataURL('image/webp').indexOf('data:image/webp') == 0;
    		}
    		// webp is not supported on older browsers such as IE.
    		return false;
		}
</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:

	<!--- 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. --->
	<cfset acceptHeader = getHttpRequestData().headers["accept"]>
	<!--- Does the header accept webp? --->
	<cfif findNoCase("webp", acceptHeader) gt 0>
		<cfset clientAcceptsWebP = true>
	<cfelse>
		<cfset clientAcceptsWebP = false>
	</cfif>
		
	<!--- 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.---> 
	<cfif application.serverSupportsWebP and clientAcceptsWebP>
		<cfset webpImageSupported = true>
	<cfelse>
		<cfset webpImageSupported = false>
	</cfif>
		
	<cfif webpImageSupported>
		<!--- Overwrite the headerBodyDividerImage var and change the extension to .webp --->
		<cfset headerBodyDividerImage = replaceNoCase(headerBodyDividerImage, '.png', '.webp')>
	</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

This entry was posted on September 17, 2019 at 2:31 AM and has received 1847 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.

 

This entry was posted on September 16, 2019 at 2:50 PM and has received 1507 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 a 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 nonessential 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 support 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.

This entry was posted on September 13, 2019 at 2:29 PM and has received 1456 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/blogand 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 is a multitude of different lazy loading libraries out there. I have tried a handful of these libraries but settled on defer.jsby shinsenter. My requirements are more extensive than usual. Along with 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. GSAPalso 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:

//* Script to defer script resources. See https://appseeds.net/defer.js/demo.html. 
// @shinsenter/defer.js */
!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:

<script type="deferjs">
// Kendo common css. Note: Material black and office 365 themes require a different stylesheet. These are specified in the theme settings.
$('head').append( $('&lt;link rel="stylesheet" type="text/css" />').attr('href', '/common/libs/kendo/styles/kendo.common.min.css') );
// Less based theme css files.
$('head').append( $('&lt;link rel="stylesheet" type="text/css" />').attr('href', '/common/libs/kendo/styles/kendo.silver.min.css') );
// Mobile less based theme file.
$('head').append( $('&lt;link rel="stylesheet" type="text/css" />').attr('href', '/common/libs/kendo/styles/kendo.silver.mobile.min.css') );
</script>


Note the defers 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.

GSAPis 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


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

<script type="deferjs" src="/blog/common/libs/greenSock/src/uncompressed/TweenMax.js"></script>
<script type="deferjs" src="/blog/common/libs/scrollMagic/scrollmagic/uncompressed/ScrollMagic.js"></script>
<script type="deferjs" src="/blog/common/libs/scrollMagic/scrollmagic/uncompressed/plugins/animation.gsap.js"></script>
<script type="deferjs" src="/blog/common/libs/greenSock/src/uncompressed/plugins/ScrollToPlugin.js"></script>
<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 Blogallows blog owners to upload picturesand 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:

/* Lazy loading image classes */
/* Initially hide the element with zero opacity */
.fade {
transition: opacity 500ms ease-in-out;
opacity: 0;
}

/* Show it with the 'shown' class */
.fade.shown {
opacity: 1;
background: 0 0;
}



Javascript:

// Lazy loading images and media.
// Callback function to add the 'shown' class into the element when it is loaded
var media_loaded = function (media) {
media.className += ' shown';
}
// Then call the deferimg and deferiframe methods
deferimg('img.fade', 300, 'lazied', media_loaded);
deferiframe('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:

// Lazy load the images.
deferimg('img.fade', 100, 'lazied', function(img) {
img.onload = function() {
img.className+=' shown';
}
});


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.

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

My biggest SEO problem....

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

This entry was posted on September 1, 2019 at 2:50 AM and has received 1132 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.

Version:

Galaxie Blog Version 3.12 (Toby's Edition) December 10th 2022 Blue Wave Dark theme