A Chumby widget is really just a SWF file that runs in the context of the Chumby framework. Developing Flash widgets for the Chumby is an interesting proposition, because Flash Lite imposes a few restrictions, your widget resides on chumby.com, and the modest processor in the Chumby tempers your appetite for special effects. The reason I find developing for the Chumby unique and fun is the ease with which you can access the accelerometer that senses the device's orientation and how much it's being shaken.
When people encounter the Chumby, the first thing they do is pick it up—after all, it looks like a bean bag. Devising creative ways to take advantage of the ability to access a Chumby's orientation is really interesting. Imagine a music player that switches genres when the user whacks the side of Chumby. Or, create a magic 8 ball widget (I previously created this app, but I'm sure you could improve upon it).
In the next section, I'll show you how to grab the values sent from the accelerometer to use in your Flash widget. The process is a bit geeky (after all, Flash Lite 3.0 doesn't have built-in support for this). The trickiest part involves understanding what the values really mean. There are values for X, Y, and Z orientation, but unlike 3D software (where × and Y are left/right and up/down and Z is depth) Chumby's accelerometer doesn't report the location in space where the Chumby resides. Rather, the three values (X, Y, and Z) reflect how the three faces of the device (side, top, and front) are currently positioned.
Imagine that the Chumby is a cube, like a single die (see Figure 4).

Figure 4. It helps to think of the Chumby as a cube when reviewing the values provided by the accelerometer
The touch screen on the front of the Chumby is on the same plane as the number 3 on the die. Consider that the range of positions (for the front face of the Chumby) varies from facing straight up (if you're holding the Chumby in your lap and you look down at the screen) to facing straight down (if you're holding the Chumby up above your head looking up at the screen). The "normal" position (as shown in the figure above) is really in the middle of that range. Of course, if you consider a full rotation there are actually two versions of "normal"—one with the screen right side up and one with the screen upside down. However, these positions are both vertical. The accelerometer simply reports the proportional position between a maximum value for face up to a minimum value for face down. The accelerometer reports the front face's position (between face down and face up) as the Y. It reports the side (where the 2 on the die appears) as the X. And it reports the top face (the 6 on the die) as the Z.
The range of motion for the × position from a maximum value (the side is facing up) to its mid-point (the side is vertical) to the minimum value (the side is facing down) is illustrated in the diagram below (see Figure 5).

Figure 5. The accelerometer provides values based on the range of motion from pointing straight up to pointing straight down
The same system is used for all three faces. (In case you're pondering the fact that a cube really has 6 faces, just think of it as 3 pairs where each face has a partner on the opposite side that is always in direct proportional orientation.)
You can use
ActionScript to access the values sent from the accelerometer through an undocumented
function: ASnative(5, 60) . You just need to add a line of
code:
var getAccelerometer:Function = ["ASnative"](5, 60);
Next, you can
set up an onEnterFrame event to repeatedly ascertain the
accelerometer's values—such as getAccelerometer(). To specify which of the X, Y, or
Z values you wish to access, you pass a single numeric parameter to the
function. For example, to grab the current × orientation, you'd call getAccelerometer(2). The following chart outlines the
values you can access and the corresponding parameter to use:
version = _accelerometer(0); timestamp = _accelerometer(1); currentX = _accelerometer(2); currentY = _accelerometer(3); currentZ = _accelerometer(4); avgX = _accelerometer(5); avgY = _accelerometer(6); avgZ = _accelerometer(7); impactX = _accelerometer(8); impactY = _accelerometer(9); impactZ = _accelerometer(10); impactTime = _accelerometer(11);
There are a
few items I want to point out about using these parameters in your code. Since the
values for avgX, avgY , and avgY are
averages of the last few readings, they yield smoother results than the
otherwise identical values currentX , currentY ,
and currentZ .
The impact
values let you sense how hard—and in which direction—the user last whacked the
Chumby. Since you'll need to query the accelerometer to read any of these
values, it is important to realize that the impact readings are from the last
impact felt. You can use impactTime to ensure that the impact is a new impact (not one you already reacted to) before displaying a graphic or an
animation.
Finally, it
is important to note that the units used for all these readings can seem odd.
The orientation values (currentX , currentY , avgX , avgY , and avgZ )
range from a minimum of about 1200 to a maximum of 2800… putting the midpoint
at 2000 (indicating that the corresponding face is vertical). The values for
impact range from 0 to 4095. A value of 0 is the maximum force if you hit one
pane, while 4095 is the maximum if you hit the opposite pane (or, I suppose you
could think of it as "pulling" the same pane). Anyway, an absence of
force is not 0 but the midpoint value—namely, 2048. The total range maps from
-5G to +5G. When you multiply any of the impact readings by
0.0024489559928062587 and then subtract 5, you'll get a value that ranges from
-5 to 5. In my experience developing for the Chumby, there are no magic
settings. You really just have to tweak the impact values until your widget
"feels" right.
I created a
utility class that is designed to insulate you from having to deal with the
details of using Chumby's accelerometer. The main feature of this class is the
ability to listen for three events: onImpact,
onRotation , and onOrientation . These events are only fired when an impact occurs, the
rotation changes by more than 1 degree, or when the orientation changes (in
order to rotate the display like the iPhone).
In the next
section I'll review the code in the provided sample file that uses these three
event listeners, but the key (besides not having to directly access the
accelerometer yourself) is that since the events only fire when a change
occurs, it is not necessary to update the graphics onscreen for every onEnterFrame event. Doing so could cause your widget to perform
poorly. The utility class includes an event called onUpdate that broadcasts all the accelerometer readings 12 times a second, and you can
create an event listener for onUpdate if that is helpful when developing
your widget project.
If you haven't already, download the sample file provided on the first page of this article . Open the FLA file in Flash CS3 Professional so you can evaluate it as I walk through the process I used to create it. Using the com.phillipkerman.chumby.utils.Accelerometer class is fairly straightforward.
//create instance:
myAccelerometer = new com.phillipkerman.chumby.utils.Accelerometer();
//set up listeners
myAccelerometer.addEventListener("onImpact", chumbyImpact);
myAccelerometer.addEventListener("onOrientation", chumbyOrientationChange);
myAccelerometer.addEventListener("onRotation", chumbyRotate);
//kick start it
myAccelerometer.init();
You really
don't have to subscribe to all these events. For example, if you only want to
listen for the onOrientation event, you can remove the other two
lines with the addEventListener methods.
Finally,
without going into the details of the Accelerometer class I wrote, let me
explain up front that all these listenable events send a property that I
created called rawValues . This is an object with properties
for each of Chumby's accelerometer readings (version, timestamp, currentX and so on) plus g-force adjusted values I created (impactX_normal, impactY_normal , and impactX_normal )
as well as offX and offY —which
I used to create the bubble-level effect. In addition, the onOrientation event sends a value for the orientation (strings "north", "east",
"west", or "south ") and the onRotation event sends a value for the
Below I've provided my handlers for the three listenable events:
function chumbyImpact( evt ){
shake_mc.play();
var values:com.phillipkerman.chumby.datatypes.RawValues = evt.rawValues;
shake_mc._x = CENTER_X + (values.impactX_normal / 1000) * (STAGE_WIDTH);
shake_mc._y = CENTER_Y + (values.impactY_normal / 1000) * (STAGE_HEIGHT);
}
The main thing to note above is how I dampen the (g-force adjusted) impact values (by dividing by 1000) and move the shake_mc clip. That movie clip contains a rough animation which, when complete, also restores the clip's original location.
function chumbyOrientationChange( evt ){
switch ( evt.orientation ){
case "north":
orientation_mc._rotation = 0;
break;
case "east":
orientation_mc._rotation = 90;
break;
case "west":
orientation_mc._rotation = 270;
break;
case "south":
orientation_mc._rotation = 180;
break;
}
}
The code above simply flips the orientation_mc clip to display so that it's always facing up. You could certainly add some fancy animation to tween this change.
function chumbyRotate ( evt ){
level_mc._rotation = evt.angle / (Math.PI/180);
var values:com.phillipkerman.chumby.datatypes.RawValues = evt.rawValues;
bubble_mc._x = CENTER_X - values.offX * CENTER_X;
bubble_mc._y = CENTER_Y - values.offY * CENTER_Y;
}
The level_mc simply rotates to match the angle—though the value needs to be converted to degrees first, before rotation. The bubble_mc (the crosshairs) is a bit odd. To see it, you need to hold the Chumby with the screen horizontal and look down at it. Alternately, you can also see the crosshairs if you hold the Chumby directly above your head and look up.
At this point you have the technical details necessary to build a widget for your Chumby. You can also share your widget with other users through chumby.com. Now the only remaining thing to do is come up with an interesting idea for a widget you want to build. I recommend browsing the widgets already available on chumby.com to get some inspiration.
Developing for Chumby offers a "fun factor" that just isn't available in the more typical web applications. The fact that your Chumby can reside in any room of your house means you can create all sorts of new applications. And for me, the fact that you can create something truly tangible using the built-in accelerometer opens up a whole new universe of user input. I really think the Chumby framework offers an exciting new playground for Flash artists, developers, and animators.
To research in more depth, check out these online resources: