The War On Waste - or Why I Focus On Libraries Saturday, November 2 2013

Skip this if you're not in a philosophical mood. It's a 1000-word soliloquy about personal goals and regrets.

Also, despite the next section, I am pro-Microsoft. My personal goal is to make developers happier and more effective on whichever platform(s) allow me to do so. Also, vendor loyalism in software often hurts the vendor - insular feedback is tremendously destructive.

Background — Waste

During my pre-2008 loyalist phase, I wasted precious years struggling with sub-par tools and writing mountains of boilerplate code. I trusted my peers (and even — foolishly — technical evangelists who've since moved on) to know which tools were best — after all, they'd been in the business much longer than I.

In defense of my peers and I; when one works exclusively on a platform of black boxes, guessing only through inference of results how they operate, one tends to stop questioning the manufacturer's guidance. Belief systems usually form, evidenced by code rituals and cargo-cult behavior. Shotgun programming proliferates, and manufacturer advertising is absorbed as if it were truth. Logic, without good data, is useless to every mind. Software, without source code, is an endless quantity of bad data. Demand source code for everything you touch. As the depth of the software stack increases, vendors who don't provide source code and transparency will reach a paralysis point.

Our planet is being revolutionized by software, but in a horrifically primitive way. Man-decades (sometimes centuries) of human creativity — per business — are spent to reinvent line-of-business software, which is used improperly (if at all) by a handful of people, and thrown away after a few years. Meanwhile, projects and organizations with a demonstrably positive influence on the world struggle to accomplish their (often unique) software goals. Despite an explosion in the number of software developers in the last two decades, very little has changed about what wastes our time and energy. For a profession that builds its own tools, it's extremely shaming that leaky abstractions are still so prevalent and that code reuse is so rare.

Software has a human cost

As developers, we tend to pour our mental energy and health into our work, leaving us so mentally drained that we are often ineffective at life itself.

I admire and envy those who balance work and life easily, who can leave problem-solving at work. I've never been able to. I shower thinking of algorithms. I shave while trying to simplify interfaces or APIs. To expel a problem from my brain, I have to read an extremely absorbing book — or replace it with a better problem.

Based on those I've met, I don't think a healthy balance is the norm for software engineers.

Software has a human cost, but that cost is different for everyone.

For me, it first struck after 4 years as a paid developer. I was 18. I take 4 medications and 5 supplements every morning as a result. My eyesight is slowly worsening. My daily visits to the gym are driven by my desperation to recover; to be around when my son is grown.

Minutes in which I have mental energy to expend are precious to me now; I don't take an hour of coding for granted.

No sum of money could justify what I (or countless others) have put into software. Our best hope to balance the scales is to translate our past, present, and future time into as much net happiness for others as we can.

Open source

I can't force the world to adopt or create open-source. Economics (at best) suggests a slow erosion of proprietary systems, but at the current rate it could be decades before we see code reuse exceed duplication — if such a thing occurs.

The sheer demand for software developers is also two-edged sword. It raises salaries, but also makes it that much harder to fund software that doesn't generate revenue.

Open-source tackles waste head-on, often fighting against massive advertising budgets with only agility and developer altruism. And somehow, it's winning against more wasteful models.

I think many confuse the enemy here. It's not Microsoft, not Apple, not closed-source software. It's waste itself. Our enemy is the squandering of human creativity, the art burning in piles of discarded one-use software.

To me, the Apache and MIT/BSD licenses are the most effective at reducing waste.

I can't find the GPL attractive. What benefit can be had by preventing closed-source programs from using your library? Developers aren't in charge of business decisions; they're just trying to get their job done. They're probably more grateful than most for what you've built, and almost as likely to contribute back. Excluding the average developer doesn't help you, them, or anyone.

There is no war between open and closed source software; only against waste.

What drives me

Lacking influence or riches, I focus on building tools.

I patch (and build) libraries to save developers time and frustration. You should, too.

Good ideas are contagious, even when implemented in the briefest manner possible. Show just one person what could be, and soon it will be what is.

Simplify the complex. Eliminate leaky abstractions. Reduce code noise. Automate the mundane.

Help developers focus on the unique parts of their challenges, and you've multiplied how much they can accomplish with their time.

Remove useless disruptions so they can spend more time in the flow, and you've made them happier people (clinically proven). Flow is one of the primary causes of happiness; leaky abstractions make it harder to be happy.

This is a goal every developer can achieve. Fixing a bug might save 100 people 30 minutes of frustration. That's a massive win in terms of net happiness, even if it was hard for you.

Just the psychological effect of a pull request is enormous to a library developer. It's a concrete validation that their library is useful, worthwhile. And as an extension, that they have been.

Select your tools with ruthlessness; repair them with consideration. Require source code to everything you touch. Optimize for net happiness.

View comments on original page...

Reading max-width, cross-browser Sunday, July 7 2013

There are 5 basic ways to access max-width

  • element.currentStyle["max-width"] (IE 6, 9, 10)
  • element.currentStyle.maxWidth (IE 7, 8, 9, 10 & Opera only)
  • window.getComputedStyle(element,null)["max-width"] (IE 9, 10, Chrome & Webkit)
  • window.getComputedStyle(element,null).maxWidth (IE 9, 10, Firefox, Opera, Chrome & Webkit)
  • window.getComputedStyle(element,null).getPropertyValue("max-width") (IE 9, 10, Firefox, Opera, Chrome & Webkit)

Test results

All BrowserStack browsers were tested for all methods. If a method is not listed, it returned undefined.

Browser method max-width: 20px max-width: 20% max-width: 2em max-width: 1cm max-width: 1in
IE6 currentStyle["max-width"] 20px 20% 2em 1cm 1in
IE7-IE8 currentStyle.maxWidth 20px 20% 2em 1cm 1in
IE9-IE10 getComputedStyle["max-width"] 20px 201.6px 32px 37.79px 96px
IE9-IE10 getPropertyValue("max-width") 20px 201.6px 32px 37.79px 96px
IE9-IE10 getComputedStyle.maxWidth 20px 201.6px 32px 37.79px 96px
IE9-IE10 currentStyle["max-width"] 20px 20% 2em 1cm 1in
IE9-IE10 currentStyle.maxWidth 20px 20% 2em 1cm 1in
Firefox 3.6-23 getComputedStyle.maxWidth 20px 201.6px 32px 37.8px 96px
Firefox 3.6-23 .getPropertyValue("max-width") 20px 201.6px 32px 37.8px 96px
Opera 11.6-12.15 getComputedStyle.maxWidth 20px 201px 32px 38px 96px
Opera 11.6-12.15 .getPropertyValue("max-width") 20px 201px 32px 38px 96px
Opera 11.6-12.15 currentStyle.maxWidth 20px 20% 2em 1cm 1in
Chrome 25-28 (win, osx) getComputedStyle["max-width"] 20px 20% 32px 37.7952766418457px 96px
Chrome 25-28 (win, osx) .getPropertyValue("max-width") 20px 20% 32px 37.7952766418457px 96px
Chrome 25-28 (win, osx) getComputedStyle.maxWidth 20px 20% 32px 37.7952766418457px 96px
All other webkit getComputedStyle["max-width"] 20px 20% 32px 37px 96px
All other webkit .getPropertyValue("max-width") 20px 20% 32px 37px 96px
All other webkit getComputedStyle.maxWidth 20px 20% 32px 37px 96px

The test page can be found here.

All other webkit browsers tested:

  • Chrome 14-19, OS X & Windows
  • Safari 5.1 & 6 (all platforms)
  • iPad 2, iPhone 4S, iPhone 5, iPad 3, iPad mini (Mobile Safari 5, 5.1, 6)
  • Amazon Kindle Fire 2, Kindle Fire HD 8.9
  • Motorola Droid 4, Razr, Razr Maxx HD, Atrix HD,
  • Samsung Galaxy S, S II, Note, S II, Note II, Tab 2 10.1, Note 10.1, Nexus,
  • Google Nexus 7
  • HTC Wildfire, Evo 3D, One X
  • LG Nexus 4, Optimus 3D
  • Sony Xperia Topo

Additional notes

Firefox 3.6 requires you specifiy null (or a psuedoselector) as the second parameter of getComputedStyle.

I tested getFloatValue, but found it highly inconsistent. It's also designed to throw errors on percentages and non-convertible units. It only operates smoothly under Firefox.

window.getComputedStyle(element,null).getPropertyCSSValue("max-width").getFloatValue(5)

Getting a max-width value (cross-browser)

Both window.getComputedStyle(element,null).maxWidth and window.getComputedStyle(element,null).getPropertyValue("max-width") are supported identically by all the browsers I tested. It's your pick on which to use.

You can fall back to currentStyle.maxWidth for IE 7/8, and finally currentStyle["max-width"] for IE6.

The following code returns values on all the browsers tested above. We ran it on DomContentReady, load, or onload, depending on browser support.

var getCssValue = function(target, hyphenProp){
  var val = typeof(window.getComputedStyle) != "undefined" && window.getComputedStyle(target, null).getPropertyValue(hyphenProp);
  if (!val && target.currentStyle){
    val = target.currentStyle[hyphenProp.replace(/([a-z])\-([a-z])/, function(a,b,c){ return b + c.toUpperCase();})] || target.currentStyle[hyphenProp];
  }
  return val;
};

Converting all CSS width units into pixels (cross-browser)

If you only support IE9 and higher, you can get by with parsing only pixels and percentages. For IE6-8, you'll need to implement parsing for all units you wish to support, as there's no conversion provided for you.

Evaluating percentages for max-width is tricky, as every css property has slightly different rules for the evaluation, and it is highly dependent on layout context.

Thankfully, there's a pretty straightforward way to convert units in context. It won't handle absolute positioning and some other edge cases, but that could be added without too much fuss.

var getCssPixels = function(target, hyphenProp){
  var val = getCssValue(target,hyphenProp);

  //We can return pixels directly, but not other units
  if (val.slice(-2) == "px") return parseFloat(val.slice(0,-2));

  //Create a temporary sibling div to resolve units into pixels.
  var temp = document.createElement("div");
  temp.style.overflow = temp.style.visibility = "hidden"; 
  target.parentNode.appendChild(temp);  
  temp.style.width = val;
  var pixels = temp.offsetWidth;
  target.parentNode.removeChild(temp);
  return pixels;
};

Here's the test page showing these 2 functions operating on all the browsers mentioned above

What was this all for? Slimmage.js, a sane responsive images solution.

View comments on original page...

The Pixel Density Explosion Wednesday, April 10 2013

Back in mid-2012, we didn't have that many unique pixel density values; just 1, 1.5, 2, and 2.25, plus variations based on zoom size. Since then, we've had an explosion of devices with high-resolution displays (adding 1.75, 2.5, 3, etc) and this continues to grow.

I'm very concerned that the current srcset and picture elements are jumping on the dppx/ pixel density bandwagon without considering that they're introducing exponential complexity for authors. If v is viewport sizes in virtual pixels we optimize for and d is pixel densities we want to support crisply without waste, we must describe v*d number of images. This is untenable.

Photographs don't care about dppx. Photos are crisp if there's a 1-1 mapping between physical and bitmap pixels. If the browser has decent scaling, additional bitmap pixels are acceptable too. It's generally a good thing if your eyes can't make out the individual pixels of a photo.

I also prefer to pinch-zoom in on a semantic photo instead of having the context cropped automatically for me. Perhaps others find it natural to have content changed on browser resize, but I find it unsettling.

Note I used the term photographs, not images. Text and layout really need virtual pixels and the dppx abstraction to ensure readability and usability - as do images that contain text or affect layout (although you should be using SVG).

What this means in terms of markup

This is where we're headed with the current picture spec and the pixel density explosion.

We're only handling the standard Bootstrap breakpoints, assuming a full-screen image, and the 6 most popular pixel densities here. We're not even handling smaller mobile devices well here, nor are we doing a great job of bandwidth optimization. We're just doing a somewhat acceptable job of delivering a crisp image that doesn't waste more than 60% of the bandwidth used.

<picture>
   <source media="(min-width: 941px)" srcset="http://cdn.company.com/site/images/1200_x1.jpg 1x, http://cdn.company.com/site/images/1200_x1_5.jpg 1.5x http://cdn.company.com/site/images/1200_x2.jpg 2x http://cdn.company.com/site/images/1200_x2_25.jpg 2.25x http://cdn.company.com/site/images/1200_x2_5.jpg 2.5x http://cdn.company.com/site/images/1200_x3.jpg 3x">
   <source media="(min-width: 768px) and (max-width: 940px)" srcset="http://cdn.company.com/site/images/940_x1.jpg 1x, http://cdn.company.com/site/images/940_x1_5.jpg 1.5x http://cdn.company.com/site/images/940_x2.jpg 2x http://cdn.company.com/site/images/940_x2_25.jpg 2.25x http://cdn.company.com/site/images/940_x2_5.jpg 2.5x http://cdn.company.com/site/images/940_x3.jpg 3x">
   <source media="(min-width: 480px) and (max-width: 767px)" srcset="http://cdn.company.com/site/images/767_x1.jpg 1x, http://cdn.company.com/site/images/767_x1_5.jpg 1.5x http://cdn.company.com/site/images/767_x2.jpg 2x http://cdn.company.com/site/images/767_x2_25.jpg 2.25x http://cdn.company.com/site/images/767_x2_5.jpg 2.5x http://cdn.company.com/site/images/767_x3.jpg 3x">
   <source srcset="http://cdn.company.com/site/images/480_x1.jpg 1x, http://cdn.company.com/site/images/480_x1_5.jpg 1.5x http://cdn.company.com/site/images/480_x2.jpg 2x http://cdn.company.com/site/images/480_x2_25.jpg 2.25x http://cdn.company.com/site/images/480_x2_5.jpg 2.5x http://cdn.company.com/site/images/480_x3.jpg 3x">
   <img src="http://cdn.company.com/site/images/480_x1.jpg" alt="">
   <p>Accessible text</p>
</picture>

This is not a good direction

So… that's pretty bad for a single image. We're definitely leaving the door open for 'art direction', but is this overhead worth it?

We've described 24 image URLs with the following physical widths:

  • 1200 - 1800 - 2400 - 2700 - 3000 - 3600
  • 940 - 1410 - 1880 - 2115 - 2350 - 2820
  • 767 - 1150 - 1534 - 1726 - 1917 - 2301
  • 480 - 720 - 960 - 1080 - 1200 - 1440

This is a crazy amount of duplication.

Despite providing so many resolutions, we're only supporting 4 breakpoints well, and either wasting or poorly supporting the rest. This is wasteful.

If we de-duplicate those resolutions, we get 9 sizes, and we can even add '640' to provide more granularity.

  • 480 - 640 - 780 - 960 - 1200 - 1440 - 1800 - 2400 - 3000 - 3600

Speaking in device pixels is good

By describing 'true pixels' instead of virtual pixels, the browser can use the same image and declaration for a 480px 2dppx viewport and a 960 1dppx viewport.

This allows us to reduce asset count by 60%, markup by ~75%, and lower our bandwidth waste tolerance from 60% to 20%.

<picture>
  <source src="http://cdn.company.com/site/images/3600.jpg" w="3600" />
  <source src="http://cdn.company.com/site/images/3000.jpg" w="3000" />
  <source src="http://cdn.company.com/site/images/2400.jpg" w="2400" />
  <source src="http://cdn.company.com/site/images/1800.jpg" w="1800" />
  <source src="http://cdn.company.com/site/images/1440.jpg" w="1400" />
  <source src="http://cdn.company.com/site/images/1200.jpg" w="1200" />
  <source src="http://cdn.company.com/site/images/960.jpg" w="960" />
  <source src="http://cdn.company.com/site/images/780.jpg" w="780" />
  <source src="http://cdn.company.com/site/images/640.jpg" w="640" />
  <source src="http://cdn.company.com/site/images/480.jpg" w="480" />
   <img src="http://cdn.company.com/site/images/480.jpg" alt="">
   <p>Accessible text</p>
</picture>

Isn't that simpler? And we're supporting a wider variety of viewport sizes and device pixel densities.

This doesn't preclude media queries or art direction, they have a place

Because pinch-to-zoom is nearly ubiqitous, I feel that it's generally a waste of time to prepare 2 photographs of identical dimensions with different content, but there is a use-case.

I am NOT advocating the removal of the media attribute from source, only that we permit a descriptive syntax as shown above, instead of an essentially imperative/declarative method. Images with text need to be modified for 2dppx and 3dppx displays; they'll be unreadable as-is. Either media="(min-device-pixel-ratio:2dppx)" or srcset can be used, but I favor using media as it simplifies and flattens the evaluation logic for both the browser and the human mind. It's good to evaluation logic in a single dimension.

srcset has the same problem.

Solving the srcset problem is more difficult (syntatically) if we also wish to support virtual viewport sizes AND pixel density selectors. Suggestions are welcome.

Clarification on scope.

Unlike my last proposal regarding the 'picture' element, I'm not trying to address the argument of viewport vs. element context for image URL selection. That's a separate concern I'll write about later.

For now, assume I'm talking about viewport-based evaluation unless I state otherwise.

This article is about device pixel ratio and why the popular use syntax should simply describe bitmap dimensions.

View comments on original page...

Browse archives...

About Nathanael

Nathanael Jones is a software engineer, husband, consultant, and computer linguist with unreasonably high expectations of inanimate objects. He refines .NET, ruby, and javascript libraries full-time at Imazen, but can often be found on stack overflow or participating in W3C community groups.

ImageResizer

If you develop websites, and those websites have images, ImageResizer can make your life much eaiser. Find out more at imageresizing.net.

Recent Tweets

| Loading recent tweets...

Imazen

I run Imazen, a tiny software company that specializes in web-based image processing and other difficult engineering problems. I spend most of my time writing image-processing code in C#, web apps in Ruby, and documentation in Markdown. Check out some of my current projects.