Satyen's Chai

By Luke Smith on December 28, 2010 10:00 PM

This chai recipe is courtesy of my coworker, Satyen Desai. He was kind enough to bring a pot of this into the office. Chai has forever been changed for me.

Ingredients (for one cup)

Instructions

  1. In a sauce pan, add water, tea, mint, cardamon, masala, and (optional) ginger/lemongrass and bring to a rolling boil
  2. Add milk, lower heat, and bring to a rolling boil again
  3. Take the pot off the heat before it overflows, let it settle back down, and put it back on. It'll continue to rise even when off the heat, so take if off before it reaches the top. Repeat a couple of times (more if you want it stronger)

Enjoy!

Grandmom's chai masala

Satyen admits you can probably get something close in Indian stores, but there's nothing like recipes from grandmom. A (I assume roughly equal) mix of the following, ground separately:

  • black pepper
  • green cardamom seeds
  • cinnamon
  • dried ginger
  • clove

Chai image via Wikipedia

I want my touch screen

By Luke Smith on October 21, 2010 7:33 AM

At yesterday's Apple event "Back to the Mac", the point was stated and reiterated that they opted not to make the laptop screen a (multi) touch interface. The reason? ergonomics. After a while, your arm gets tired. Then after a while, your arm wants to fall off, Steve Jobs said.

I wonder if he was spouting party line or he actually believed that ergonomics was a valid reason to detach the interface from the thing being interfaced. I know I don't.

If the iPhone, iPod, and iPad proved anything with the touch interface, it's that a direct touch interface is incredibly natural. My three year old knows how to operate my iPod Touch, but I didn't teach him. In contrast, I spent some time introducing him to the idea that when you move this thing (the mouse), this other thing up here on the screen moves (the cursor). And then, when you click on this button, it does something. He didn't get it. A disembodied, foreign, two-step process will never beat a direct correlation to a natural process.

The other day, a coworker noted that after working with an iPad for a week, he started touching the monitor attached to his laptop. He's been using computers for a long time. Years of experience with mice and trackpad (even multi-touch gesture supporting trackpads) undone in a week. Why? Because people use their hands to manipulate what they see by touching it.

Do I buy the fatigue argument? You bet.

Unlike the iPad, laptop screens are inherently vertical. Making them horizontal, or more horizontal, would result in a lot of time spent looking (further) down, which results in bad posture and fatigue. And of course, touching a deeply inclined screen would just tip the laptop over. The reason it's ok for the iPad to rely on a touch screen is that it is not aimed at extended operation with extensive interaction. Reading a book or watching a movie takes time, but not much interaction. Productivity on an iPad basically requires a horizontal physical keyboard or a lot of intelligence in the software being interacted with, such that complex tasks can be done with little interaction. Having to physically support the thing while using it, or craning down over it to use both hands, is less than ideal, especially for extended interaction. (Full disclosure: I don't own an iPad, and have used one very little, but I have both an iPhone and iPod Touch)

I'm curious what people that have the iPad stand and keyboard think about the usability of the device while fully docked. Did they abandon the touch screen for a mouse or trackpad? If so, did they completely abandon it?

It seems to me the debate isn't over yet. Natural interfaces are a huge win for usability, but we apparently still need to learn how to bridge the gap to make them a huge win for productivity. I hope Apple and others are not content to leave the discussion at "it can't be done; bad ergonomics".

Liam quotes of the day

By Luke Smith on December 23, 2009 8:04 PM

I want to break it into five pieces. — in reference to many many things.

I have hummus in my nose. — while being rocked to sleep.

And my personal favorite needs a little context. The metal drum in our drier cracked a few weeks ago, and the other day the crack expanded to a point where it was dangerous for our clothes. Fortunately we'd bought the extended warranty, so the repair was free.

The new drum came in a very large bubble wrap bag. We played with the bag for a while; I swung him around in there, slung him over a shoulder, and bounced him up and down in it.

Then it got late and he melted. We weren't terribly successful convincing him that eating is a great cure for being hungry, so there was a lot of screaming. At one point he cried, in the same breath, I want soup. I don't want soup! (but that's not the quote). There was a lot of this.

We negotiated that if he gets in his diaper and pajamas that I'll carry him to his room in the bubble wrap bag. This was a turbulent activity, but eventually, he gets in his PJs. He's completely out of his senses and flinging arms and legs everywhere, then he hops in and I pick up the bag and carry him to his bedroom. While in transit, the crazies suddenly disappear and a calm sweet child's voice utters It's nice in here.

Then the crazies came back.

CommonJS require(..) API is a poor fit for client side JS

By Luke Smith on November 24, 2009 10:54 PM

This post is in response to David Flanagan's client side JS implementation of CommonJS's module pattern/API.

There are several performance considerations with script/module/dependency loading.

  1. script tags in markup block the page progression
  2. synchronous XHR blocks the js thread and therefore the UI responsiveness
  3. script tags added to the DOM via js do not block page progression, but their execution order must be managed
  4. there are severe performance penalties for numerous http requests (http meta/overhead, time, and browser's concurrent resource request limits)
  5. "discovered" dependency resolution == chained http requests (sync or async), so deep deps trees could take a long time to resolve. A reqs B reqs C could result in load A, then load B, then load C, then execute C, then B, then A, then execute requiring code (see #2 and #4).

The CommonJS API has great qualities for environments that don't suffer from the sorts of IO costs and conditions that make sync behavior a Bad Idea(tm) in client side scripting. The implementation as one-expression-per-requirement is especially bad because it is functionally equivalent to multiple blocking script nodes locking up the page progress or rendered UI, which is somewhere between bad and horrible. To avoid this, require might be a registration function of an async loader to bundle multiple require(..) calls from the same thread, but that breaks the require-then-use contract.

The YUI 3 module and use(..) arch is basically an async implementation of key parts of this API. This is especially obvious in the YUI.add(moduleName, function (Y) { /* add stuff to Y */ }, ver, meta); form of registering modules where you could easily substitute export for Y.

The big differences are that Y.use(..) accepts multiple modules/requirements to avoid the one-expression-per-requirement, it does pre-fetch dependency resolution based on a managed dependency metadata tree, and the modules are loaded asynchronously using as few requests as possible (the Yahoo! CDN combo service makes 1 request the common case), executing a callback when complete.

The less than optimal part of this is the dependency resolution tactic, especially for modules from third parties. Delaying dependency calculation for a module until that module is loaded would kill performance, so all dependency metadata for library modules is stored in a meta module for pre-fetch resolution. We already know this won't scale in a non-impactful way, and of course this doesn't address third party modules. For third party modules, we require dependency metadata in the configuration for requiring code, which is less than ideal. For optimal performance and client side footprint, dependency resolution basically has to happen on the server side or be integrated into a pre-deployment build step.

All that said, my main point is that as exciting as the recent server-side JS movement has been, the synchronous require(..) pattern is a poor fit on the client side for the high performance demands of today's sites and RIAs.

Test if a font is installed via JavaScript

By Luke Smith on May 26, 2009 8:45 AM

There are a few threads available on the web with regard to how to test if a font is installed on a client machine, but I'm not satisfied with any of those that I found. The reason being that they each seem to use a single common font as a baseline measurement and compare the dimensions of some reasonably complex string rendered in the common font against the same string rendered in the test font.

This irks me for a number of reasons:

  1. It assumes the common font is installed
  2. It only confirms the dimensions of the tested font don't coincide with the common font
  3. All of those I saw rerendered the same element over and over, triggering an unnecessary number of reflows

If you want to check if a font is installed, start with a provable test. The system default monospace font should render a string in different dimensions than the system default sans-serif font. But you can test this.

(function () {
var div = document.createElement('div'),
    different = false;

div.innerHTML = '<span style="...;font-family: sans-serif">some string</span>' +
                '<span style="...;font-family: monospace">some string</span>';

document.body.insertBefore(div, document.body.firstChild);

different = div.childNodes[0].offsetWidth != div.childNodes[1].offsetWidth;

document.body.removeChild(div);

alert(different);
})();

Using this tested assertion as a baseline, let the browser tell you if the test font is installed by using the default behavior of font stacks. Create two elements with the same content ("ii" has been sufficient in my tests), each styled with a font-family of the test font followed respectively by the two control fonts that you know to render to different dimensions.

<b style="font: normal 10px/1 'FONT_X', sans-serif !important">ii</b>
<b style="font: normal 10px/1 'FONT_X', monospace !important">ii</b>

The two elements will render to the same dimensions if and only if the the font is installed.

Here's my approach:

function testFont(name) {
    name = name.replace(/['"<>]/g,'');

    var body  = document.body,
        test  = document.createElement('div'),
        installed = false,
        template =
            '<b style="display:inline !important; width:auto !important; font:normal 10px/1 \'X\',sans-serif !important">ii</b>'+
            '<b style="display:inline !important; width:auto !important; font:normal 10px/1 \'X\',monospace !important">ii</b>',
        ab;

    if (name) {
        test.innerHTML = template.replace(/X/g, name);

        test.style.cssText = 'position: absolute; visibility: hidden; display: block !important';

        body.insertBefore(test, body.firstChild);

        ab = test.getElementsByTagName('b');

        installed = ab[0].offsetWidth === ab[1].offsetWidth;

        body.removeChild(test);
    }

    return installed;
}

The code above assumes the string "ii" renders to different width in monospace than sans-serif, but the important part is that this can be tested if you don't feel comfortable with that assumption. Additionally, containing two elements in an absolutely positioned div should limit the scope of the two requisite reflows if the browser is smart about it.

If you want to test more fonts, create more template entries in the innerHTML all at once before attaching the div to the DOM, then just loop through the entry pairs. This will preserve the two reflow impact.

Here's a test page.

And here's the code in a GitHub gist. It's likely this is more up to date than the snippet above.

More entries

 

ls.n

LucasSmith.name

Luke and Heidi

I'm Luke. I am a front end engineer at Yahoo! on the YUI team.

Mostly I write about code stuff, but occassionally I'll mix in some real life. You've been warned.

Recent Posts

Archives

Tags

Feeds

Subscribe to feed Recent entries

Content licensed under Creative Commons

Code licensed under BSD license

©2005 - 2010 Lucas Smith

Powered by Movable Type