Author Archives: simonsarris

HTML5 Canvas fillText considered harmful

I’m writing a book on HTML5, including Canvas! Click here for more information.

I have in my possession sufficient evidence for the conviction of Canvas’ fillText().

The charge? Performance murder.


Reig-05
fillText() resisting arrest

I suspected fillText() of being slow ever since adding blocks of text as interactive objects to a Canvas library I am working on. I’ve also begun writing a canvas performance guidebook, so the sacrament of confirmation was doubly in order.

Since most of the slowness in canvas applications isn’t drawing something once, but redrawing it many times per second, I wondered if there were a faster way to redraw text than using the Canvas context’s fillText() method.

This is especially pertinent when drawing vertical text, since every new letter has to be on another line, and thus another call to the fillText() method.

I puzzled around with ways to avoid all the calls, and settled on trying something: Instead of calling fillText() to redraw my text objects each frame, I would instead create a new canvas (one never added to the DOM) for every single text object, and call fillText() on each object only once, drawing the text to its personal canvas.

Then, every time I wanted to (re)draw that text object, I would call drawImage() on my real canvas, passing in the object’s personal canvas, instead of using fillText(). This gives exactly the same per-pixel drawing, but is it faster?

I wrote a small, distilled test for the purpose, hypothesizing that it probably wasn’t worth doing for normal text but may be worth doing for vertical text. There’s a link at the end if you’re interested in trying it yourself.

There are a lot of scenarios though: What if I had just one text object that had to be redrawn 60 times a second? Surely – if drawImage() is faster than fillText() – having an additional canvas would be a small price to pay in overhead.

But what if I had 200 text objects? Maybe each text object having a canvas would cause it to be far slower.

I won’t bore you much longer, the definitive answer is this: If you are redrawing several text objects more than just a few times, it is far faster to give each object its own canvas and use drawImage() instead of fillText(). Not just for vertical text, but for any text, even if its just drawing a single character.

Some of the tests are described below in my poorly-made graphs. The first row of graphs are closer to real-use scenarios. Please note the Y-axis range (milliseconds) changes from graph to graph.

Six of the tests. Smaller bars are better.

As you can see, drawing a text object of just 1 character several hundred times is far faster in Chrome, Firefox and, IE9 when using drawImage(), and thus very much worth it to have that extra canvas around.

The second row of graphs were some fooling around to find the limit of this method’s utility. If you have 200 text objects (and thus 200 additional canvases) of just 1 character each, and render them only once, using drawImage() becomes a bit of a waste. But drawing those 200 objects merely three times and you can see that it will be faster in chrome to use drawImage() than to use fillText(). Firefox and IE9 are a little more resilient here, but they don’t take very long at all for drawImage() to become worth it.

So giving each text object its own Canvas is a pretty workable solution even if the objects are redrawn very few times. If you are trying to achieve 30 frames per second, giving each text object its own Canvas and using drawImage() is a no-brainer, and should probably a default consideration.

Of course Canvas applications can differ wildly, and you should draw up tests for your own situation before deciding how to draw text. Perhaps exceptionally large canvases or exceptionally small text yield different results, or perhaps memory on the targeted machine means that so making so many canvases are an untenable proposition, and so on.

Here’s a link to the test I made. I would of course appreciate any corroboration of results that anyone is willing to do.

EXTRA CREDIT:

You’d need to do more work to get this to work with scaling text, since you’d have to size the temporary canvases appropriately. It does however have the added benefit of fixing the scaling + rotating text bug that currently exists in Chrome and Opera.

Looking over Google’s Cr-48 Chrome OS laptop

Merely two days after I apply to test drive a Chrome OS notebook, one arrives in the mail! It was packaged inside a very happy box.

The contents were minimal: One laptop, one battery, one charger, one single page instruction card and one business card from Intel.

Challenge accepted.

The exterior of the laptop has been detailed by plenty of others, so I won’t get into that. gdgt.com already has a decent page on it.

But before I started to really use it I wanted to poke around inside. Looking over the external didn’t yield anything interesting: Just a few screws. Oh, and this?

To the right of the battery connector was a tiny black piece of tape-like material. Peeling it away revealed a small switch. For the sake of curiosity I flipped it.

Uh oh. Apparently the switch controls something memory related. (note that there’s no hard drive in the Cr-48, just flash memory).

I turned the computer off and turned the switch back, hopeful that I might not have totally destroyed Google’s gift. Thankfully, it seems to have a way to reset itself.

I had to re-enter my wireless password and login credentials, but otherwise the notebook was back to “new.” Not that there’s much of a difference – one of the things Google is trying to tout here is how easily one can change computers and still keep all of one’s “stuff” in order.

Despite taking out all the bottom (and behind-the-foot) screws the laptop would not come apart, at least with gentle amounts of pressure. I wasn’t keen on continuing further without first having a few days of experiencing the OS, so seeing the Atom Inside will have to wait.

On the actually-using-the-product side of things, I’ve really enjoyed it so far. The keyboard is unorthodox, but mostly suited to a browser-only experience. The F1-F12 are gone in favor of a set of laptop+browser specific keys. Unfortunately, the page-up/down keys are forgone in favor of ALT + Up/Down. Similarly, the delete key is hidden. I didn’t see any way to get home/end functionality, which unfortunately are necessary for any real writing in my common usage of computers.

I was surprised to see that Chrome OS is really nothing but Chrome. No file browser, no terminal (EDIT: Yes there is! Press CTRL+ALT+T for a very limited terminal), no desktop, just a web browser. For most users this is enough, since I suspect that the majority of computer users in the world use a web browser, a word processor, and a chat client or two, all of which can now have counterparts that exist in the browser. I think it would have great potential as a writing laptop were it not for the loss of the dedicated home/end/delete keys.

After using the Cr-48, it almost seems silly that netbooks attempt to be full computers. Google’s beta laptop instead seems to be what netbooks should have been from the start: a platform for all the thousands of browser apps, big and small, that make up the vast majority of common user’s tasks and games, without any of the complications or cruft of an entire operating system dragging its feet around.

ChromeOS is certainly no Windows/Ubuntu/OSX replacement, but with both WiFi and cellphone-network enabled internet as options, I think it will be a great little thing to bring around, from cafes to traveling. Certainly better than my powerhouse HP Envy, after all, since I’m not going to be playing much Starcraft 2 while drinking soup and coffee or touring Belgium.

Instead I’ll be reading email and the paper and reddit, or any number of things that I do on computers most of the time, all from a browser that just happens to be an operating system.

Selectable Shapes Part 2: Resizable, Movable Shapes on HTML5 Canvas

Wait! This tutorial is rather old and I’ve given part one a major overhaul. You should really read (or at least start with) the new one here instead.

In the first tutorial I showed how to create a basic data structure for shapes on an HTML5 canvas and how to make them selectable, draggable, and movable. In this second part we’ll be reorganizing the code a little bit and adding selection handles so we can resize our Canvas objects.

The finished canvas will look like this:


This text is displayed if your browser does not support HTML5 Canvas.

Click to select a box. Click on a selection handle to resize. Double click to add new boxes.


This article’s code is written primarily to be easy to understand. It isn’t optimized for performance because a little bit of the drawing is set up so that more complex shapes can easily be added in the future.

In this tutorial we will add:

  1. Code for drawing the eight boxes that make up the selection handles
  2. Some small adjustments to the draw code
  3. Code to be run on every mouse move event
  4. Code for changing the mouse cursor when it is over a selection handle
  5. Code for resizing

Drawing the selection handles

The eight selection handles are unique in that each one allows you to resize an object in a different way. For instance clicking on the top-middle one will only let you make it taller or shorter, but the top-right one will allow you to make it taller, shorter, as well as more wide or narrow.

Like all decidedly unique things, we’ll want to keep track of them.

// New, holds the 8 tiny boxes that will be our selection handles
// the selection handles will be in this order:
// 0  1  2
// 3     4
// 5  6  7
var selectionHandles = [];

Previously we had the variables mySelColor and mySelWidth for the selection’s color and width. Now we also add variables for selection box color and size:

var mySelBoxColor = 'darkred'; // New for selection boxes
var mySelBoxSize = 6;

Draw’s new home

Draw is still its own function but most of the code has been moved out of it. We’re going to make our Box class start to look a little more classy by letting boxes draw themselves. If you haven’t seen this syntax before, it adds the draw function to all instances of the Box class, creating a someBox.draw() we can call on boxes. To clear up confusion, our old draw loop will be renamed mainDraw.

// New methods on the Box class
Box.prototype = {
  // we used to have a solo draw function
  // but now each box is responsible for its own drawing
  // draw() will call this with the normal canvas
  // myDown will call this with the ghost canvas with 'black'
  draw: function(context, optionalColor) {
    // ... (draw code) ...
  } // end draw

}

This draw code is lifted from the old draw method but with a few additions for the selection handles. We check to see if the current box is selected, and if it is, we draw the selection outline as well as the eight selection boxes, their places based on the selected object’s bounds.

In the Init() function we need to add the selectionHandles[] initialization as well as a new event. In the past, myMove was only activated if you pressed down with the mouse, and became deactivated as soon as the mouse was released. Now we need myMove to be active all the time.

// new code in init()
canvas.onmousemove = myMove;

// set up the selection handle boxes
for (var i = 0; i < 8; i ++) {
  var rect = new Box;
  selectionHandles.push(rect);
}

Our new main draw loop is now very slimmed down:

function mainDraw() {
  if (canvasValid == false) {
    clear(ctx);
    
    // draw all boxes
    var l = boxes.length;
    for (var i = 0; i < l; i++) {
      boxes[i].draw(ctx); // we used to call drawshape, but now each box draws itself
    }
    
    canvasValid = true;
  }
}

Doing this reorganization isn't too important now, but it will be useful later on if we have many different types of objects draw themselves. After all, rectangles and (for instance) lines are not drawn in the same way, so if we can put all the custom drawing code in the object's own class we can keep things better organized.

myMove revisited

Before I talk about myMove lets take a look at two new variables added to the top of our code that signal whether or not a box is being dragged and if so, from which selection handle.

var isResizeDrag = false;
var expectResize = -1; // New, will save the # of the selection handle if the mouse is over one.

isResizeDrag seems simple enough, it works almost identically to isDrag. expectResize will be a number between 0 and 7 to indicate which selection handle is currently active. If none is active (the default), we'll set it to -1.

In most programs that have selection handles (such as the edges of your browser) it is nice to have the mouse cursor change to show that an action can be performed. To do this we are going to have to ask where the mouse is located all the time and see if it is over one of our eight selection handles. Remember that above we made myMove active all of the time and Now we have to add code to it:

// Happens when the mouse is moving inside the canvas
function myMove(e){
  if (isDrag) {
    getMouse(e);
    
    mySel.x = mx - offsetx;
    mySel.y = my - offsety;   
    
    // something is changing position so we better invalidate the canvas!
    invalidate();
  } else if (isResizeDrag) {
    // ... new code to talk about later.
  }
  getMouse(e);
  // if there's a selection see if we grabbed one of the selection handles
  if (mySel !== null && !isResizeDrag) {
    for (var i = 0; i < 8; i++) {
      // 0  1  2
      // 3     4
      // 5  6  7
      
      var cur = selectionHandles[i];
      
      // we dont need to use the ghost context because
      // selection handles will always be rectangles
      if (mx >= cur.x && mx <= cur.x + mySelBoxSize &&
          my >= cur.y && my <= cur.y + mySelBoxSize) {
        // we found one!
        expectResize = i;
        invalidate();
        
        switch (i) {
          case 0:
            this.style.cursor='nw-resize';
            break;
          case 1:
            this.style.cursor='n-resize';
            break;
          case 2:
            this.style.cursor='ne-resize';
            break;
          case 3:
            this.style.cursor='w-resize';
            break;
          case 4:
            this.style.cursor='e-resize';
            break;
          case 5:
            this.style.cursor='sw-resize';
            break;
          case 6:
            this.style.cursor='s-resize';
            break;
          case 7:
            this.style.cursor='se-resize';
            break;
        }
        return;
      }
      
    }
    // not over a selection box, return to normal
    isResizeDrag = false;
    expectResize = -1;
    this.style.cursor='auto';
  }

So if there is something selected and we are not already dragging, we will execute some code to see if the mouse position is over one of the selection boxes. If it is, give the mouse cursor the correct arrow. If the mouse isn't over a selection box, make sure we change it back to the normal pointer.

You'll also notice that at the start, after "if (isDrag)" we have a new test, "else if (isResizeDrag)." isResizeDrag becomes true if two conditions are met:

  1. expectResize is set to one of the selection handle numbers (is not -1)
  2. we have pressed down the mouse

In other words, it only happens if the mouse is over a selection handle and has been pressed. We add a tiny bit of code to myDown to make this work.

// Happens when the mouse is clicked in the canvas
function myDown(e){
  getMouse(e);
  
  //we are over a selection box
  if (expectResize !== -1) {
    isResizeDrag = true;
    return;
  }

  // ... the rest of myDown

}

Anyway, getting back to myMove. We are looking for the "else if (isResizeDrag)" to see what happens when this is true.

function myMove(e){
  if (isDrag) {
    
    // ...

  } else if (isResizeDrag) {
    // time ro resize!
    var oldx = mySel.x;
    var oldy = mySel.y;
    
    // 0  1  2
    // 3     4
    // 5  6  7
    switch (expectResize) {
      case 0:
        mySel.x = mx;
        mySel.y = my;
        mySel.w += oldx - mx;
        mySel.h += oldy - my;
        break;
      case 1:
        mySel.y = my;
        mySel.h += oldy - my;
        break;
      case 2:
        mySel.y = my;
        mySel.w = mx - oldx;
        mySel.h += oldy - my;
        break;
      case 3:
        mySel.x = mx;
        mySel.w += oldx - mx;
        break;
      case 4:
        mySel.w = mx - oldx;
        break;
      case 5:
        mySel.x = mx;
        mySel.w += oldx - mx;
        mySel.h = my - oldy;
        break;
      case 6:
        mySel.h = my - oldy;
        break;
      case 7:
        mySel.w = mx - oldx;
        mySel.h = my - oldy;
        break;
    }
    
    invalidate();
  }
  
  // ... rest of myMove
}

We see a bunch of arithmetic dealing with precisely how each handle will resize the box. Handle #6 is middle-bottom, so it only resizes the height of the box. Handle #1, on the other hand, is the middle top. It has to resize both the Y-axis co-ordinate as well as the height. If #1 only changed the Y-axis, then dragging it upwards would just look like the entire box is being dragged upwards. If it just resized the height, the top of the box would stay in the same position and we certainly don't want that if the top is what we intended to move!

That's pretty much everything. Try it out yourself above or see the demo and download the full source on this page.

So that wasn't too bad. A few long chunks were added but not because of complexity, just because each of the eight selection handles is uniquely placed and does a unique resizing task.

If you would like to see this code enhanced in future posts (or have any fixes), let me know how in the comments.

Making and Moving Selectable Shapes on an HTML5 Canvas: A Simple Example

I’m writing a book on HTML5, including Canvas! Click here for more information.

Wait! I’ve given this tutorial a major overhaul and you should really read the new one here instead.

This is part one in a series. Part 2 can be found here.

This tutorial will show you how to create a simple data structure for shapes on an HTML5 canvas and how to have them be selectable. The finished canvas will look like this:


This text is displayed if your browser does not support HTML5 Canvas.

Click to drag boxes. Double click to add new boxes.


This article’s code is written primarily to be easy to understand. It isn’t optimized for performance, though a little bit of the drawing is set up so that more complex shapes can easily be added in the future.

We’ll be going over a few things that are also essential to game-development (drawing loop, hit testing), and in later tutorials I will probably turn this example into a small game.

The HTML5 Canvas

A Canvas is made by using the <canvas> tag in HTML:

   
    This text is displayed if your browser does not support HTML5 Canvas.
   

A canvas isn’t smart: it’s just a place for drawing pixels. If you ask it to draw something it will do so and then immediately forget everything about what you have just done. Because of this we have to keep track ourselves of all the things we want to draw (and re-draw) each frame.

So we’ll need to add:

  1. Code for keeping track of objects
  2. Code for initialization
  3. Code for drawing the objects as they are made and move around
  4. Code for mouse events

Keeping track of what we draw

To keep things simple for this example we will just make a rectangular object called Box. We’ll also make a method for creating Boxes a little easier.

// holds all our rectangles
var boxes = []; 

//Box object to hold data for all drawn rects
function Box() {
  this.x = 0;
  this.y = 0;
  this.w = 1; // default width and height?
  this.h = 1;
  this.fill = '#444444';
}

//Initialize a new Box, add it, and invalidate the canvas
function addRect(x, y, w, h, fill) {
  var rect = new Box;
  rect.x = x;
  rect.y = y;
  rect.w = w
  rect.h = h;
  rect.fill = fill;
  boxes.push(rect);
  invalidate();
}

Initialization

I’m going to add a bunch of variables for keeping track of the drawing and mouse state. I already added boxes[] to keep track of each object, but we’ll also need a var for the canvas, the canvas’ 2d context (where wall drawing is done), whether the mouse is dragging, width/height of the canvas, and so on. We’ll also want to make a second canvas, for selection purposes, but I’ll talk about that later.

var canvas;
var ctx;
var WIDTH;
var HEIGHT;
var INTERVAL = 20;  // how often, in milliseconds, we check to see if a redraw is needed

var isDrag = false;
var mx, my; // mouse coordinates

 // when set to true, the canvas will redraw everything
 // invalidate() just sets this to false right now
 // we want to call invalidate() whenever we make a change
var canvasValid = false;

// The node (if any) being selected.
// If in the future we want to select multiple objects, this will get turned into an array
var mySel; 

// The selection color and width. Right now we have a red selection with a small width
var mySelColor = '#CC0000';
var mySelWidth = 2;

// we use a fake canvas to draw individual shapes for selection testing
var ghostcanvas;
var gctx; // fake canvas context

// since we can drag from anywhere in a node
// instead of just its x/y corner, we need to save
// the offset of the mouse when we start dragging.
var offsetx, offsety;

// Padding and border style widths for mouse offsets
var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;

// initialize our canvas, add a ghost canvas, set draw loop
// then add everything we want to intially exist on the canvas
function init() {
  canvas = document.getElementById('canvas');
  HEIGHT = canvas.height;
  WIDTH = canvas.width;
  ctx = canvas.getContext('2d');
  ghostcanvas = document.createElement('canvas');
  ghostcanvas.height = HEIGHT;
  ghostcanvas.width = WIDTH;
  gctx = ghostcanvas.getContext('2d');
  
  //fixes a problem where double clicking causes text to get selected on the canvas
  canvas.onselectstart = function () { return false; }
  
  // fixes mouse co-ordinate problems when there's a border or padding
  // see getMouse for more detail
  if (document.defaultView && document.defaultView.getComputedStyle) {
    stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingLeft'], 10)      || 0;
    stylePaddingTop  = parseInt(document.defaultView.getComputedStyle(canvas, null)['paddingTop'], 10)       || 0;
    styleBorderLeft  = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderLeftWidth'], 10)  || 0;
    styleBorderTop   = parseInt(document.defaultView.getComputedStyle(canvas, null)['borderTopWidth'], 10)   || 0;
  }

  // make draw() fire every INTERVAL milliseconds.
  setInterval(draw, INTERVAL);
  
  // add our events. Up and down are for dragging,
  // double click is for making new boxes
  canvas.onmousedown = myDown;
  canvas.onmouseup = myUp;
  canvas.ondblclick = myDblClick;
  
  // add custom initialization here:
  
  // add an orange rectangle
  addRect(200, 200, 40, 40, '#FFC02B');
  
  // add a smaller blue rectangle
  addRect(25, 90, 25, 25, '#2BB8FF');
}

Drawing

Since our canvas is animated (boxes move over time), we have to set up a draw loop as I did in the init() function.

We have to draw at a frame rate, maybe every 20 milliseconds or so. However, redrawing doesn’t just mean drawing the shapes over and over; we also have to clear the canvas before every redraw. If we don’t clear it, dragging will look like the box is making a solid line because none of the old box-positions will go away.

Because of this, we clear the entire canvas before each Draw frame. This can get expensive, and we only want to draw if something has actually changed within our framework, so we will consider the canvas to be either valid or invalid.

If everything just got drawn, the canvas is valid and there’s no need to draw again. However, if we do something like add a new Box or try to move a box by dragging it, the canvas will get invalidated and draw() will do a clear-redraw-validate.

This isn’t the only way to optimize drawing, after all clearing and redrawing the entire canvas when one little box moves is excessive, but canvas invalidation is the only optimization we’re going to use for now.

// While draw is called as often as the INTERVAL variable demands,
// It only ever does something if the canvas gets invalidated by our code
function draw() {
  if (canvasValid == false) {
    clear(ctx);
    
    // Add stuff you want drawn in the background all the time here
    
    // draw all boxes
    var l = boxes.length;
    for (var i = 0; i < l; i++) {
        drawshape(ctx, boxes[i], boxes[i].fill);
    }
    
    // draw selection
    // right now this is just a stroke along the edge of the selected box
    if (mySel != null) {
      ctx.strokeStyle = mySelColor;
      ctx.lineWidth = mySelWidth;
      ctx.strokeRect(mySel.x,mySel.y,mySel.w,mySel.h);
    }
    
    // Add stuff you want drawn on top all the time here
    
    
    canvasValid = true;
  }
}

As you can see, we go through all of boxes[] and draw each one, in order from first to last. This will give the nice appearance of later boxes looking as if they are on top of earlier boxes. After all the boxes are drawn, a selection handle (if there's a selection) gets drawn around the box that mySel references.

If you wanted a background (like a city) or a foreground (like clouds), one way to add them is to put them before or after the main two drawing bits. There are better ways though, like using multiple canvases, but we won't go over that here.

Mouse events

Now we have objects, initialization, and a loop that will constantly re-draw when needed. All thats left is to make the mouse do things upon pressing, releasing, and double clicking.

With our MouseDown event we need to see if there are any objects we could have clicked on. And we don't want just any object; selections make the most sense when we only grab the top-most object.

Now we could do something very easy and just check the bounds of each of our boxes - see if the mouse co-ordinates lie within the boxes width and height range - but that isn't as extendable as I'd like. After all, What if later we want to select lines instead of boxes? Or select triangles? Or select text?

So we're going to do selection in a more general way: We will draw each shape, one at a time, onto a "ghost" canvas, and see if the mouse co-ordinates lie on a drawn pixel or not.

A ghost canvas (or fake canvas, or temporary canvas) is a second canvas that we created in the same size and shape as our normal one. Only nothing from it will ever get seen, because we only created it in code and never added it to the page. Go back and look at ghostcanvas and its context (gctx) in the init() function to see how it was made.

// Happens when the mouse is clicked in the canvas
function myDown(e){
  getMouse(e);
  clear(gctx); // clear the ghost canvas from its last use

  // run through all the boxes
  var l = boxes.length;
  for (var i = l-1; i >= 0; i--) {
    // draw shape onto ghost context
    drawshape(gctx, boxes[i], 'black');
    
    // get image data at the mouse x,y pixel
    var imageData = gctx.getImageData(mx, my, 1, 1);
    var index = (mx + my * imageData.width) * 4;
    
    // if the mouse pixel exists, select and break
    if (imageData.data[3] > 0) {
      mySel = boxes[i];
      offsetx = mx - mySel.x;
      offsety = my - mySel.y;
      mySel.x = mx - offsetx;
      mySel.y = my - offsety;
      isDrag = true;
      canvas.onmousemove = myMove;
      invalidate();
      clear(gctx);
      return;
    }
    
  }
  // havent returned means we have selected nothing
  mySel = null;
  // clear the ghost canvas for next time
  clear(gctx);
  // invalidate because we might need the selection border to disappear
  invalidate();
}

myMove and myUp are pretty self explanatory. the var isDrag becomes true if myDown found something to select, and it becomes false again when the mouse is released (myUp).

// Happens when the mouse is moving inside the canvas
function myMove(e){
  if (isDrag){
    getMouse(e);
    
    mySel.x = mx - offsetx;
    mySel.y = my - offsety;   
    
    // something is changing position so we better invalidate the canvas!
    invalidate();
  }
}

function myUp(){
  isDrag = false;
  canvas.onmousemove = null;
}

There are a few little methods I added that are not shown, such as one to correctly get the mouse position in a canvas. You can see and download the full demo source here.

Now that we have a basic structure down, it is easy to write code that handles more complex shapes, like paths or images or video. Rotation and scaling these things takes a bit more work, but is quite doable with the Canvas and our selection method is already set up to deal with them.

If you would like to see this code enhanced in future posts (or have any fixes), let me know how in the comments.

Part 2 of this tutorial is about resizing the shapes and can be found here.

A Nanopitter?

While reading a story regarding nanopillars I kept thinking of a sort of minuscule caterpillar. I wondered if anyone else had thought the same so (somehow) ended up searching for the word nanopitter on Google. I think this was the first time in a long time that such a short query yielded zero results!

So now I must wonder how long it will take before this page appears on Google as the sole result.

Edit: Apparently, about a day.

Customizing the HP Envy Quick Launch keys

I bought an envy about a month ago and have been working through the usual new laptop quirks. There are plenty of hard-to-notice-until-bought pros and cons that I might pour over some time, but right now I’d just like to mention my findings with respect to the Envy Quick Launch keys that are located on the left of the keyboard.

By default these keys open up a mail program, HP’s MediaSmart SmartMenu, a web browser, printing, and the Windows calculator program. You can see them on the left side of the keyboard, with labels I’ll explain in a moment:




There’s no way in the HP software to customize the buttons, so I did a little bit of fishing around and found some useful information.

My original intent was to make a small program that would sit in the system tray and simply have a menu to enable/disable all the buttons at will. The buttons are nice, but they aren’t the most useful thing to have enabled when one is trying to play a full-screen game. I’d keep getting sent back to the desktop because I accidentally opened a calculator instead of pressing Ctrl. Similar problems abound with Photoshop, or any program that demands constant use of Tab and Ctrl.

Making a small key-capture program I found out that the first odd-one out was the Print key, which literally just sends the key press for LeftCtrl and P. The second odd one is the wave- looking key, the SmartMenu button, which I’ll address later.

The others had strange key names: BrowserHome, calc.exe, and LaunchMail. At that point I decided to look for those names in the registry and came upon some interesting values:

There are 5 entries in a registry folder corresponding to some of the Quick Launch keys as well as mysterious nonexistant keys. First I’ll mention what the real keys do and how to change them.

Editing the keys

Go to the start bar in Windows 7, type “regedit” and press enter. If you’re unfamiliar with the Windows registry then please be careful, messing with values here can cause a lot of errors.

Navigate to the registry folder:

ComputerHKEY_LOCAL_MACHINESOFTWAREMicrosoft
WindowsCurrentVersionExplorerAppKey

In the folder you’ll see more folders, the three we care about are:

  • 15 calls for the Assocation of ‘mailto’
  • 18 calls for a ShellExecute of ‘calc.exe’
  • 7 calls for the Assocation of ‘http’ (opens the browser or program associated with http)

These are the mail, calculator, and internet buttons respectively. If you want to change them, simply modify the values inside each folder. Associations will open the program that is tied to the protocol or file type. For these two buttons, the ‘mailto’ association opens Windows Live Mail (by default), and the ‘http’ association opens whatever browser is your default. ShellExecute on the other hand is the same as typing something into Run and pressing enter.

An example: Let’s say you want the mail button to open Notepad: Go into the folder named 15, rename “Association” to “ShellExecute”, double click ShellExecute to modify the key’s data and enter “notepad.exe”, replacing “mailto.”

Want to disable a button? Just clear out the ShellExecute or Association data. For the mail button you would double click the Association and replace “mailto” with nothing.

What about the wave key?

The wave-looking key actually opens a program called ‘HP MediaSmart SmartMenu.’ You can uninstall this from Programs and Features in the Control Panel if you wish. I haven’t yet found a way to do anything else with it.

What were those other registry entries?

  • 16 calls for the Assocation of .cda, this seems to be a nonexistant key that was intended to be media-centric
  • 17 calls a ShellExecute on ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}, which is the ParsingName for “My Computer” in Folder Descriptions, so I suppose it was a My Computer button

These keys were probably programmed for but never implemented in the final Envy design.

None of this gives us an easy way to disable the keys temporarily, though, so I guess I’ll have to release a small program later to let people temporarily disable and re-enable them on the fly.

Also, if anyone has any information about the Wireless On/Off key above the “+” key, please let me know. I’d really like to find a way to temporarily disable that, or better yet only make it work when “fn” is pressed, but so far haven’t found anything useful on the key.

A solution to the ATI Hammer Text Corruption Problem

Many people with newer ATI cards have been having trouble using Valve’s Hammer, the Source SDK’s level editor. Specifically, the text that tells you what the dimensions of an object are has become unreadable.

This topic has been discussed in threads like this one.

I did a great deal of digging, most of it ending in other users complaining about how neither Valve or ATI seem to care about this issue – and indeed I emailed Valve and they emailed me back saying that they don’t support the SDK. Alas, I have found a solution to the Text corruption many of us have seen in Hammer!

The solution:

1. Download a program known as ATI Tray Tools. I had to use the Beta since I’m running Windows 7.

2. Right click its icon, Go to

3D >
Additional Options >
and check the box for Alternate Pixel Center

This will fix the error.

Happy level making!

EDIT:
Apparently, while this tweak fixes Hammer, it breaks all the Source games in a similar text-garbled manner. You’ll have to turn it off again to play Source games.

How to play Age of Empires II on Windows Vista and Windows 7

I love Age of Empires II. I love all real-time strategy games, but Age of Empires II must be among the finest I’ve ever played, especially as a multiplayer game.

Unfortunately, getting Age of Empires to work in this modern day is a bit of a pain. The game was built on network technology known as IPX that has since gone the way of the dinosaurs. So far gone, in fact, that Microsoft decided not to include it in Vista and Windows 7. All of my computers are using Windows 7 now, so getting the game to play over LAN proved to be difficult.

I didn’t want to deal with trying to hodge-podge together an IPX installation, so I decided to look for ways for the TCP/IP method of gameplay to work. This method rarely worked for me, and when it did it only worked on some computers and not others.

Apparently, all I needed to do was assign all the computers that were playing in the game to have static IP addresses. If you don’t know what that is, there’s a link to a guide at the bottom of this article. Set static IPs on every computer that will be playing, not just the host.

Then, on the computer hosting, select Internet TCP/IP Connection for DirectPlay and NOT the LAN option. For some reason, selecting the LAN option caused issues. However, all the computers that will be joining can just select the LAN option.

Assuming there are no firewall issues, you should be good to go. I tested this with Windows 7 only machines, and only over LAN. If anyone has tried this as described and couldn’t get it to work, I’d like to hear from you.

Have fun playing!

Here’s a wonderful guide from PortForward.com on setting up a static IP.

Quite possibly the best time to buy a MacBook Pro?

The fruit-themed computer manufacturer has finally made an effort to bring the MacBook Pro into the modern era. Boasting new Core i5 and i7 series processors and an updated (albeit last generation) graphics card, the new MacBook Pro sells at a curiously similar price to the MacBook Pro of last week.

Take a look at what’s changed. I left the HP Envy 15 in for a comparison to a modern laptop with non-Apple brand pricing. Once again I am using close-to-base-model because a larger deviation hurts the Mac price far more, but can also be done after-market for cheaper, and is thus pretty unfair.

Part MacBook Pro 15
(1 week ago)
MacBook Pro 15
(Today)
HP Envy 15
Processor Intel Core2 Duo T9600 @ 2.80GHz
(Passmark score: 1995)
(2 cores)
Intel Core i7 M 620 @ 2.60GHz
(Passmark score: 2871)
(2 cores)
Intel Core i7 720QM @ 1.60GHz
(Passmark score: 3296)
(4 cores)
RAM 4GB DDR3 4GB DDR3 4GB DDR3
Video NVIDIA GeForce 9600M GT
512MB of GDDR3
120 gigaFLOPs
OpenGL 2.1
NVIDIA GeForce GT 330M
512MB of GDDR3
182 gigaFLOPs
OpenGL 2.1
Mobility Radeon HD 5830
1GB of GDDR3
800 gigaFLOPs
OpenGL 3.2
Screen 15.4″
1440×900
15.4″
1680×1050
15.6″
1920×1080
HDD 500GB SATA
@ 7200 rpm
500GB SATA
@ 7200 rpm
500GB SATA
@ 7200 rpm
Price: $2,399.00 $2,399.00 $1,749.99

Little else has changed on the Pro aside from the processor, video card, and max screen resolution. The supported DirectX and OpenGL are still still 10.1 and 2.1 respectively, and the laptop’s price remains unchanged*.

The updated video card is NVIDIA’s GeForce GT 330M, which is an over-clocked GT 240M, which itself is just an over-clocked GT 230M (June 2009), which explains the aging DirectX and OpenGL versions.

Nonetheless, thanks to Apple’s staunch refusal to discount hardware that was already two years old, it is a significantly better time to buy a MacBook Pro than it was just one week ago, be it new or used. Every other laptop manufacturer has had to reduce prices on aged models or introduce newer models in order to stay competitive, but it seems that Apple is immune to Father Time, perhaps because its consumer base practices an isolationism unseen since the days of Hai jin.

This is of course not a bad thing in itself. If you’re running an all-Mac shop then right now is quite possibly the best time to buy a MacBook Pro. Yesterday, however, was quite possibly the worst.

* As configured above. I believe the minimum price for a base 15-inch MacBook Pro is slightly less than it was previous.