Tools for video on the web

There are two ways to use video on the web:

  • You can upload your content to a hosting server like Youtube or Vimeo and let them worry about compression and delivery
  • Compress and upload your video files to your server and assume the cost and the consequences
    We will work on the second idea, hosting our own video and dealing with storage, transmission and accessibility considerations.

Creating and using HTML5 video

While not essential to video compression, it is important to know how to create the tags HTML5 video uses to put video on the page

The Video chapter from Dive into HTML5 provides a good introduction on the subject and will save us from detailed explanations here.

What you need to know

The size of video files can be reduced by changing one or bot of the following:

  • The codec you choose to use. Some codecs work better than others at a given bitrate
  • The dimensions of the file. Larger sizes usually take higher bitrates to maintain quality
  • The bitrate (how many bits does it take to render a second of video) the higher the bitrate the larget the size of the video file

What codec to use

The choice of codecs is influenced by your target platforms and licensing considerations.

In the list below the number indicates the earliest version of the browser that supported the codec. I’m assuming that later versions will support the codec as well.

One of the biggest considerations when working with MPEG-4/H.264 and most MPEG formats is that they are emcumbered by patents. The licensing is different depending on whether you’re using the codec for commercial or non-comercial work, whether you’re streaming or not and how many subscribers access your content.

Bitrate

Video data rates are given in bits per second. The data rate for a video file is the bitrate. So a data rate specification for video content that runs at 1 megabyte per second would be given as a bitrate of 8 megabits per second (8 mbps). The bitrate for an HD Blu-ray video is typically in the range of 20 mbps, standard-definition DVD is usually 6 mbps, high-quality web video often runs at about 2 mbps, and video for phones is typically given in the kilobits (kbps). For example, these are the targets we usually see for H.264 streaming:

  • LD 240p 3G Mobile @ H.264 baseline profile 350 kbps (3 MB/minute)
  • LD 360p 4G Mobile @ H.264 main profile 700 kbps (6 MB/minute)
  • SD 480p WiFi @ H.264 main profile 1200 kbps (10 MB/minute)
  • HD 720p @ H.264 high profile 2500 kbps (20 MB/minute)
  • HD 1080p @ H.264 high profile 5000 kbps (35 MB/minute)

From Encoding.com’s [Understanding bitrates in video files](http://help.encoding.com/knowledge-base/article/understanding-bitrates-in-video-files/)

Note that 1 byte = 8 bits. To convert 35MB into bits we need to do the following:

35 X 1024 X 1024 X 1024

This becomes even more important for ebooks.

When we choose to include the file in our book we need to consider the total size of the book and the possible cost the readers will face when downloading the book to their devices.

When linking to the video hosted elsewhere we assume that the user has WiFI on their device and that they’ll be ok with absorbing the bandwidth cost.

Tools

Rather than rehash the tools and techniques I use, I’ll refer you to the video section of Dive into HTML5. Although it’s starting to show its age it’s still an awesome resource.

If your video is already compressed then there might or might not be much gain from compressing again. Test and measure results.

The quick and dirty

When I need to compress video in a hurry, my go to tool is Miro Video Converter a GUI front end to ffmpeg.

It is a crossplatform tool released under GPL. If you are comfortable using ffmpeg directly, Miro has made the its ffmpeg settingsavailable so you can use them in your command line experiments.

Rather than write my own instructions I’ll refer you to the guide I used to learn and still use when working with Miro: Dive into HTML5

The more complex

Handbrake provides a more complex video capture and encoding solution without having to jump into commercial software (that’s next.) It also allows you to capture unencrypted DVD and Blue Ray video.

Dive into HTML has a good tutorial on using Handbrake to capture and compress video.

Sorenson Squeeze

Sorenson Squeeze is my option when I need the most felixbility and am willing to pay the cost both in terms of money (it is comercial software) and learning curve (the price you pay for the flexibility you get.)

I don’t recommend this as a beginner or even intermediate tool. Squeeze, more than the other tools discussed require a lot of experimentation or prior knowledge in order to use corectly. Once you settle on a preset, using Squeeze is as simple as Miro.

In my opinion where Squeeze shines is the creation of multiple bitrate files for Apple’s HTTP Live Streaming, Microsoft’s Smooth Streaming, or DASH.

A good tutorial is the tutorial from Streaming Media Producer

Web Content Optimization: RAIL: Puting the user first

Rail: Putting the user first

Paul Irish and Paul Lewis wrote an article for Smashing Magazine outlining a new way to look at performance by analyzing how users perceive the speed of your site.

Paul Irish presented about RAIL at SFHTML5’s AMP Project Presentations. He began by asking the question What is slow?

  • Is changing the DOM slow?
  • Are Javascript animations slower than CSS animations?
  • Is loading a script in the head of the document slow? Is it slower than loading the script before closing the body?

The answer to all these questions is it depends. As Paul explains in the Smashin Magazine article:

While it’s true that different operations take different amounts of time to complete, it’s hard to say objectively whether something is slow or fast without the context of when it’s happening. For example, code running during idle time, in a touch handler or in the hot path of a game loop each has different performance requirements. Put another way, the people using your website or app have different performance expectations for each of those contexts. Like every aspect of UX, we build for our users, and what they perceive is what matters most. In fact, number one on Google’s ten things we know to be true is “Focus on the user and all else will follow.”
Asking “What does slow mean?,” then, is really the wrong question. Instead, we need to ask “What does the user feel when they’re interacting with the things we build?”
From Smashing Magazine

The Numbers Change

Based on Jakob Nielsen’s research we can throw down some numbers as the basis for performance measurement and how to quantify user’s perception.

  • 100 miliseconds (0.1 seconds) is the window where the user feels reaction is automatic
  • 1 second is the limit for where users feel like a continual uninterrupted thought
  • 10 seconds is the limit for users to keep attention on the task they are performing. Longer times will make the user go away
  • 16 miliseconds in how long you have to perform a task to keep a target of 60 frames a second (1000 ÷ 60 = 16)

The RAIL Performance Model

Using the numbers above we can create reasonable performance metrics that take user perception into account.

The TL;DR from the Smashing Magazine article:

  • RAIL is a model for breaking down a user’s experience into key actions (for example, tap, drag, scroll, load).
  • RAIL provides performance goals for these actions (for example, tap to paint in under 100 milliseconds).
  • RAIL provides a structure for thinking about performance, so that designers and developers can reliably target the highest-impact work.

RAIL is based on four aspects:

  • Response
  • Animation
  • Idle
  • Load

The 4 components of the RAIL performance model

We’ll discuss each of these aspects in turn:

Response

If a user taps anywhere in your web content they should get a response as quickly as possible, before they notice the lag. The last thing we want is for the user to tap on a button and the response take so long that the user wonders if anything happened.

The main type of response interaction is taping (either with your finger on a mobile device or clicking your mouse or pointer device in laptop or desktop system.)

To respon appropriately we need to
– Provide a response within 100 miliseconds
– Ideally provide the complete response but, if this is not possible then provide some indication that we’re working on the request until it is completed

Animation

It’s impossible to look around the web and not see animations. Visual animations, scrolling, drag and visual transitions between sections of content; we all take for granted that these will be smooth and it’s easier to notice when the animations fail than most any other content.

Examples of animation:
– Visual animation: entrance and exit animations, tweened state changes, and loading indicators.
– Scrolling: The user starts scrolling and lets go and the page is flung.
– Drag: Animation might follow as a result, as when panning a map or pinching to zoom.

This is where the magic 60 frames per second number comes in. In order to make it happen all animations for a given frame need to happen in 16.6 miliseconds or under (1000 ÷ 60 = 16.6)

Idle

I was surprised when I saw idle as a component of a performance model until I realized this: When the browser is idle is the best time to actually make it work on long lasting processes.

To make sure we don’t turn the idle work into an all consuming thread hog, the RAIL model suggest we break the work in chunks that last no more than 50 miliseconds so that, when the user decides to interact with the page we can respond within 100 miliseconds and not be caught in the middle of an expensive task.

Load

When we talk about Page Load in the context of a meaningful and responsive first paint. We want to get the critical path content on to the user’s browser as quickly as possible and afterwards the page has to remain responsive, the user can tap, scroll and continue to interact with the site as it finishes loading.

To accomplish this we want to make sure we load the critical path content in a second or less. We need to prioritize the critical rendering path and we can defer non-essential loads to periods of idle time (or lazy-loading them on demand).

Performance Goals

Response Animation Idle Page Load
Tap to paint in less than 100 milliseconds. Each frame completes in less than 16 milliseconds. Use idle time to proactively schedule work. Satisfy the “response” goals during full load.
  Drag to paint in less than 16 milliseconds. Complete that work in 50-millisecond chunks. Get first meaningful paint in 1,000 milliseconds.

Business Impact

It is not just technology. Performance has been known to affect conversion and retention. Paul and Paul provided the following information in the Smashing Magazine’s article:

Dcoumentation and Learning Resources

Web Content Optimization: Performance Budget and Page Speed Test

Web Content Optimization: Performance Budget

Performance budgeting allows developers to user performance as another part of our design and builld process. There is no reason why a site should weigh in at over 2 mega bytes or take more than 10 seconds, and over 30 seconds in a mobile device, to load.

In his post Performance and Design, Brad Frost writes that:

Too often, any talk of web performance quickly ventures into the land of heavy geekery. Terms like DNS lookups, Gzipping, minifying, far future expires headers, caching, ETags and more are thrown around and consequently lose the attention of most non-techy people. This perpetuates a mentality that performance is solely a technical concern that only developers need to concern themselves with.

It’s time for us to treat performance as an essential design feature, not just as a technical best practice.

I wonder if we really consider the weight that all the third party content we add to our site and how it affects the user experience. We need to take action in some fashion. Performance Budgeting is a first step in that direction.

Optimization Tools

Goggle Page Speed Insights service and Web Page Test provide ways to test your web content and give you feedback on what needs to be changed to improve your site’s performance.

Page Speed Insights

Page Speed Insights gives you a coarse evaluation of your site on both mobile and desktop browsers. This is important because Google now uses “mobile friendliness” as a criteria on ranking your site in the search results.

Web Page Test

Page Speed Test gives you a finer view f your site’s performance and provides additional functionality that is not available through Insights alone. My favorite parts of the toolset are the thumbnails and the performance waterfall, similar to that in Chrome Dev Tools

As an example I did a Page Speed Test of a project I’m currently working on. It is hosted in Github which helps explain some of the results.

Page Speed Test Result
Page Speed Test Result

What I find most useful are the thubnails. They give me a timelapse representation of the site’s load time. I usually set them to between 0.5 and 1 second.

This has helped me realie the insane amount of “crap” I put on my web content. Granted most of that content is externa to the site such as Codepen examples and Youtube and Vimeo streaming clips. They still add up and they still make the content slower to appear on screen in a usable way which, in the end, is the only metric that really matters

Page Speed Test Results -- Thumbnails and Waterfall
Page Speed Test Results — Thumbnails and Waterfall

Grunt perfbudget

If you use Grunt for your web build process, there is a plugin that will let you use Page Speed Test as part of your build process.

The task requires you to get an API key from Web Page Test to run.

The task configuration looks like this (API key has been removed)

      perfbudget: {
        all: {
          options: {
            url: 'https://caraya.github.io/books-as-apps/typography.html',
            key: 'USE YOUR OWN',
            budget: {
              visualComplete: '4000',
              SpeedIndex: '1500'
            }
          }
        }
      },

The two parameters I’ve configured are:

  • visualComplete how long (in miliseconds) the page take to look done (be visually complete, even if there are more elements to load
  • speedIndex assigned budget for Speed Index

A sample run of the task configured above looks like this:

14:10:26] [email protected] images 16552$ grunt perfbudget
Running "perfbudget:all" (perfbudget) task
-------------------------------------------
Test for https://caraya.github.io/books-as-apps/typography.html       FAILED
-------------------------------------------
visualComplete: 3100 [PASS]. Budget is 4000
SpeedIndex: 1702 [FAIL]. Budget is 1500
Summary: http://www.webpagetest.org/result/150828_TC_162F/

I was lenient with myself and still was only 900 miliseconds below my visuallyComplete target adn was over the speedIndex target which caused the task to fail. Back to the drawing board.

It is also worth noting that these numbers are not absolutes. While we should be strict in enforcing and following a performance budget there may be situations where the user’s perception of speed may be more important than the actual results of Web Page Test. We’ll discuss RAIL and user perception as a performance consideration in a later post.

Web Content Optimization: Images

Unless we are careful when creating them or manipulating them with Photoshop or its equivalent images can become huge very quickly. If we’re not careful when saving the images we will find ourselves with images that are way larger than they need to be.

Photoshop (or your favorite image editor) save for the web

The simplest way to compress your images is to save for web. I can hear your laughter but it still holds true that most of us will use Photoshop, or something similar to it, to create and manipulate images. So it makes sense to figure out how to best compress your images in the platform you create them or manipulate them.

One thing to remember is that compressing your image or reducing its dimensions can cause issues like pixelation and visible loss of quality. I would never say don’t compress your images but I will always say use your judgement when doing so.

Photoshop CC

Photoshop has always provided a save for web feature. Information for (older versions of) Adobe Photoshop CC can be found in the Adobe Help Website.

Newer versions of Photoshop 2015 still maintain the save for web for leagcy compatbility but now recommend to use the export as feature instead.

GIMP

The GNU Image Manipluation Program (GIMP) provides the facilities to both scale and compress images. It is an open source equivalent to Photoshop and it’s available for most major platforms: Mac, Windows and Linux.

Command line tools

Lately I’ve been working on command line tools and build workflows using Grunt and Gulp. Because it is command line based and uses Node as the driving force Gimp and Photoshop do not work or would take much longer to develop than it would take using the tools below.

Imagemagick

ImageMagick is an older image processing and manipulation package first released in 1987. While commands like Imagemin (discussed below) are geared only towards image compression, Imagemagick can perform multiple tasks.

The following grunt task illustrate some of the things you can do with Imagemagick.
– The first section will create multiple resilution files, like those you’d use for responsive images, for each jpg file with the indicated extension
– The second session will take the files from the test directory, resize them and copy the resulting file into the test/resized directory
– The last section will take the test/resizeme.jpg file, resize it to a 25x25px image and save the resulting file as resizeme-small.jpg

    "imagemagick-hisrc":{
        dev:{
            files:"**/*-2x.jpg",
            suffix:["-2x","-1x","-low"],
        }
    },
    "imagemagick-resize":{
            dev:{
            from:'test/',
            to:'test/resized/',
            files:'resizeme.jpg',
            props:{
                width:100
            }
        }
    },
    "imagemagick-convert":{
        dev:{
        args:['
            test/resizeme.jpg',
            '-resize', '25x25', 
            'test/resized/resizeme-small.jpg']
        }
    }

Imagemin

Imagemin is a command line image compression utility that is primarily designed to work as command line and GUI application(Mac, Linux and Windows) as well as build workflows such as Grunt and Gulp task runners

It comes bundled with the following optimizers:

I’ve seen mozjpegrecomended as an alternative compressor for JPEG images.

An example of imagemin as a Grunt task. Please note that the task below only works with jpeg and png images. I know that I don’t have any gif or svg images in the project.

  imagemin: {
     png: {
          options: {
            optimizationLevel: 7
         },
      files: [ {
         // Set to true to enable the following options…
        expand: true,
        // cwd is 'current working directory'
        cwd: 'images/',
        src: ['**/*.png'],
        dest: 'dist/images/',
        ext: '.png'
     }
]},

     jpg: {
        options: {
            progressive: true,
            use: [mozjpeg()]
        },
        files: [{
            // Set to true to enable the following options…
            expand: true,
            // cwd is 'current working directory'
            cwd: 'images/',
            src: ['**/*.jpg'],
                dest: 'dist/images/',
                ext: '.jpg'
             }]
     }
 },

And the (partial) result of running the task above:

Running "imagemin" task

Running "imagemin:png" (imagemin) task
Verifying property imagemin.png exists in config...OK
Files: images/Mosaic_Netscape_0.9_on_Windows_XP.png 
  -> dist/images/Mosaic_Netscape_0.png
Files: images/Navigator_1-22.png 
  -> dist/images/Navigator_1-22.png
Files: images/cern-webservices-archive.png 
  -> dist/images/cern-webservices-archive.png
Files: images/coffee2.png 
  -> dist/images/coffee2.png
Files: images/font-in-chrome-mac.png 
  -> dist/images/font-in-chrome-mac.png
Files: images/font-in-spartan-win-vm.png 
  -> dist/images/font-in-spartan-win-vm.png
Files: images/font-terms.png 
  -> dist/images/font-terms.png
Files: images/fonts-in-use-example.png 
  -> dist/images/fonts-in-use-example.png
Files: images/full-width-translated-object.png
  -> dist/images/full-width-translated-object.png
Files: images/hyperreal-org-1996-archive.png 
  -> dist/images/hyperreal-org-1996-archive.png
Files: images/kerning.png 
  -> dist/images/kerning.png
Files: images/mag_001.png
  -> dist/images/mag_001.png
Files: images/mag_002.png 
  -> dist/images/mag_002.png
Files: images/mag_003.png 
  -> dist/images/mag_003.png
Options: interlaced, optimizationLevel=3, progressive

&lt;img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="✔" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" 
  src="//s.w.org/images/core/emoji/72x72/2714.png"/>"  
  src="//s.w.org/images/core/emoji/72x72/2714.png"&gt;    
  images/Mosaic_Netscape_0.9_on_Windows_XP.png (saved 824 B - 1%)
&lt;img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="✔" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" 
  src="//s.w.org/images/core/emoji/72x72/2714.png"/>"  
  src="//s.w.org/images/core/emoji/72x72/2714.png"&gt; 
  images/font-terms.png (saved 23.85 kB - 39%)
&lt;img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="✔" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" 
  src="//s.w.org/images/core/emoji/72x72/2714.png"/>" 
  src="//s.w.org/images/core/emoji/72x72/2714.png"&gt; 
  images/cern-webservices-archive.png (saved 22.08 kB - 19%)
&lt;img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="✔" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" 
  src="//s.w.org/images/core/emoji/72x72/2714.png"/>"    
  src="//s.w.org/images/core/emoji/72x72/2714.png"&gt; 
  images/Navigator_1-22.png (saved 19.64 kB - 63%)
&lt;img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="✔" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" 
  src="//s.w.org/images/core/emoji/72x72/2714.png"&gt; 
  images/kerning.png (saved 854 B - 5%)
&lt;img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="✔" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" 
  src="//s.w.org/images/core/emoji/72x72/2714.png"/>"
  src="//s.w.org/images/core/emoji/72x72/2714.png"&gt; 
  images/font-in-spartan-win-vm.png (saved 77.43 kB - 43%)
&lt;img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="✔" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" 
  src="//s.w.org/images/core/emoji/72x72/2714.png"/>"
  src="//s.w.org/images/core/emoji/72x72/2714.png"&gt; 
  images/font-in-chrome-mac.png (saved 31.71 kB - 23%)
&lt;img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="✔" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" 
  src="//s.w.org/images/core/emoji/72x72/2714.png"/>"
  src="//s.w.org/images/core/emoji/72x72/2714.png"&gt; 
  images/coffee2.png (saved 17.9 kB - 13%)
&lt;img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="✔" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" 
  src="//s.w.org/images/core/emoji/72x72/2714.png"/>"  
  src="//s.w.org/images/core/emoji/72x72/2714.png"&gt; 
  images/fonts-in-use-example.png (saved 23.59 kB - 13%)
&lt;img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="<img draggable="false" class="emoji" alt="✔" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" src="//s.w.org/images/core/emoji/72x72/2714.png"/>" 
  src="//s.w.org/images/core/emoji/72x72/2714.png"/>"  
  src="//s.w.org/images/core/emoji/72x72/2714.png"&gt; 
  images/hyperreal-org-1996-archive.png (saved 21.84 kB - 17%)
. . .
Minified 14 images (saved 799.53 kB)

Web Content Optimization: HTML

Tasks that minimize HTML are available for Grunt and Gulp and both are based on the HTML minifier tool.

Option Description Default
removeComments Strip HTML comments false
removeCommentsFromCDATA Strip HTML comments from scripts and styles false
removeCDATASectionsFromCDATA Remove CDATA sections from script and style elements false
collapseWhitespace Collapse white space that contributes to text nodes in a document tree. false
conservativeCollapse Always collapse to 1 space (never remove it entirely). Must be used in conjunction with collapseWhitespace=true false
preserveLineBreaks Always collapse to 1 line break (never remove it entirely) when whitespace between tags include a line break. Must be used in conjunction with collapseWhitespace=true false
collapseBooleanAttributes Omit attribute values from boolean attributes false
removeAttributeQuotes Remove quotes around attributes when possible. false
removeRedundantAttributes Remove attributes when value matches default. false
preventAttributesEscaping Prevents the escaping of the values of attributes. false
useShortDoctype Replaces the doctype with the short (HTML5) doctype false
removeEmptyAttributes Remove all attributes with whitespace-only values false
removeScriptTypeAttributes Remove type="text/javascript" from script tags. Other type attribute values are left intact. false
removeStyleLinkTypeAttributes Remove type="text/css" from style and link tags. Other type attribute values are left intact. false
removeOptionalTags Remove unrequired tags false
removeIgnored Remove all tags starting and ending with < %, %>, < ?, ?> false
removeEmptyElements Remove all elements with empty contents false
lint Toggle linting false
keepClosingSlash Keep the trailing slash on singleton elements false
caseSensitive Treat attributes in case sensitive manner (useful for custom HTML tags.) false
minifyJS Minify Javascript in script elements and on* attributes (uses UglifyJS) false (could be true, false, Object (options))
minifyCSS Minify CSS in style elements and style attributes (uses clean-css) false (could be true, false, Object (options))
minifyURLs Minify URLs in various attributes (uses relateurl) false (could be Object (options))
ignoreCustomComments Array of regex’es that allow to ignore certain comments, when matched [ ]
processScripts Array of strings corresponding to types of script elements to process through minifier (e.g. text/ng-template, text/x-handlebars-template, etc.) [ ]
maxLineLength Specify a maximum line length. Compressed output will be split by newlines at valid HTML split-points.
customAttrAssign Arrays of regex’es that allow to support custom attribute assign expressions (e.g. '<div flex?="{{mode != cover}}"></div>') [ ]
customAttrSurround Arrays of regex’es that allow to support custom attribute surround expressions (e.g. <input {{#if value}}checked="checked"{{/if}}/>) [ ]
customAttrCollapse Regex that specifies custom attribute to strip newlines from (e.g. /ng\-class/)
quoteCharacter Type of quote to use for attribute values (‘ or “)

Installation Instructions

From NPM for use as a command line app:

npm install html-minifier -g

From NPM for programmatic use:

npm install html-minifier

From Git:

git clone git://github.com/kangax/html-minifier.git
cd html-minifier
npm link .

Usage

For command line usage please see html-minifier --help

Node.js

var minify = require('html-minifier').minify;
var result = minify('<p title="blah" id="moo">foo</p>', {
  removeAttributeQuotes: true
});
result; // '<p title=blah id=moo>foo</p>'

Grunt Task

grunt.initConfig({
  htmlmin: {                                     // Task
    dist: {                                      // Target
      options: {                                 // Target options
        removeComments: true,
        collapseWhitespace: true
      },
      files: {                                   // Dictionary of files
        'dist/index.html': 'src/index.html',     // 'destination': 'source'
        'dist/contact.html': 'src/contact.html'
      }
    },
    dev: {                                       // Another target
      files: {
        'dist/index.html': 'src/index.html',
        'dist/contact.html': 'src/contact.html'
      }
    }
  }
});

Gulp Plugin

var gulp = require('gulp');
var htmlmin = require('gulp-htmlmin');

gulp.task('minify', function() {
  return gulp.src('src/*.html')
    .pipe(htmlmin({collapseWhitespace: true}))
    .pipe(gulp.dest('dist'))
});

HTML versus web components

Over the past few weeks I’ve been looking at Polymer and web components again and it led me to wonder if web components are a better solution to transport and performance issues we are discussing in this series. I’m not sure what the answer is yet but it’d be an interesting research project.