Hacking Mac OS X Panther

Editor's note: Rael Dornfest, coauthor of Mac OS X Panther Hacks, has selected these three hacks from the book for your sampling pleasure. The first two detail how to find anyone in your Address Book who has an Amazon Wish List, and how to build a GUI to your Unix scripts with a bit of Perl or Python; the third is just for fun. Enjoy.

Fulfill Wishes with Address Book

This hack will find anyone in your Address Book
that has an Amazon Wish List, but it's up to you buy
them something!

The Macintosh operating system has always
been a rather scriptable environment. Many applications support
scriptable events that let you automate tasks or integrate separate
programs. With Mac OS X, there are even more opportunities for
scripting, now that the ultimate scriptable environment, Unix, is
under the hood. But why limit yourself to the Desktop? The
Mac's script-friendly environment can integrate your
Desktop application with web applications such as Amazon.

This hack is a quick example that loosely integrates the Mac Address
Book with Amazon. Using AppleScript
and an underlying Unix tool called
curl, this script finds people in your Address
Book that also have an Amazon Wish List. This script might help you
find a gift for someone you know, or it might just give you a
different perspective on someone by finding what
they're interested in. Most importantly, though, it
shows how Desktop applications can become smarter by integrating with
web applications.

The Code

Open the Script Editor (Applications→AppleScript), enter the
following code, and save the script with a suitably snappy name, like
Get Wish Lists:

(*
Find Wish Lists in Address Book
The script loops through people in your Address Book, checking Amazon
to see if they have a Wish List. If their Wish List is found, you
have the option to view it in your default browser.
by Paul Bausch
*)
-- set some variables for the curl command
set userAgent to "Mozilla/5.0 (Macintosh; U; PPC Mac OSX; en-us)"
set curlCommand to "curl -i -b -A -L \"" & userAgent & "\" "
-- open Address Book and loop through people
tell application "Address Book"
repeat with thisPerson in the people
set thisName to name of thisPerson
repeat with thisAddress in email of thisPerson
set thisEmail to value of thisAddress
-- build the URL that will search for the Wish List
set baseURL to "http://www.amazon.com/gp/registry/search.html"
set thisURL to baseURL & "/?type=wishlist\\&field-name=" & thisEmail
-- use curl to fetch the search page
set thisWishPage to do shell script curlCommand & thisURL
-- if the search returns what appears to be a match, prompt the user
if thisWishPage contains "&id=" then
set theAction to display dialog thisName & ¬
" has an Amazon wishlist." buttons {"View", "Ignore"}
if button returned of theAction is "View" then
-- Find the ID on the page
set beginID to (offset of "&id=" in thisWishPage) + 4
set endID to (offset of "'s Wish List" in thisWishPage) - 1
set thisWishID to get text beginID thru endID of thisWishPage
if thisWishPage contains "&id=" thenset beginID to 1
set endID to (offset of "\">" in thisWishID) - 1
set thisWishID to get text beginID thru endID of thisWishID
-- Open the default browser to the Wishlist page
tell me to open location "http://www.amazon.com/o/registry/" ¬
& thisWishID
end if
end if
end repeat
end repeat
end tell

The script starts by setting some variables for
curl that will be used later.

TIP:
If you'd like learn more about
curl
and what these settings mean, open a Terminal window
(Applications→Utilities→Terminal) and type
man curl. You'll get the complete
documentation that explains what all of these command switches do.
Or, you can read "Downloading Files from the Command
Line" [Mac OS X Hacks, Hack #61].

Then, the script loops through all of the entries in the Address
Book, looking for email addresses. It uses each individual email
address to build a URL that queries Amazon for a Wish List associated
with that address. This is where the script needs to step out of its
AppleScript confines to the larger world of Unix commands. With the
do shell script command, curl
contacts Amazon to see if this person has a Wish List.

If a Wish List is found, the display dialog
command brings up a window like the one in Figure 2-6, with two options: View or Ignore.

Figure 2-6. Script prompt

Clicking View tells the script to open the Wish List with the default
browser. Because this command takes place within the tell
application "AddressBook" block, the
context-switching tell me command needs to precede
AppleScript's open location
function.

Running the Code

You can
run the code directly from the Script
Editor by clicking the Run button.

To always have it a click away from your Address Book, move the
script to the Library/Scripts/Address Book
Scripts folder. This way, it'll be
available from your Address Book's Scripts menu. Run
the script at any time by selecting Script Menu→Get Wish
Lists from the Address Book application's menu
bar.

—Paul Bausch

Add a Dab of GUI to Unix Scripts

Build a graphical dialog front-end to your Unix
scripts with just a snippet of Perl or Python glue code.

When it
comes to combining Unix scripting and GUI,
more often than not, a little dab will do. Such was the case with the
Mac OS X Installer I was building for
Blosxom (http://www.blosxom.com), my
designed-for-Mac-yet-runs-anywhere weblog [Hack #30] application.

I'd built a nice Mac OS X package (http://developer.apple.com/documentation/DeveloperTools/Conceptual/SoftwareDistribution),
which turned my otherwise rather Unix-y application into a
double-clickable install. However, I still needed to set a
configuration setting or three and didn't want to
require my users to resort to editing Perl scripts by hand. So, I set
about divining an elaborate—and not a little bit
grotty—scheme involving one part shell scripting, two parts
Perl, and a pinch of AppleScript.

TIP:
The output generated by echo in the preceding
shell snippet and print in the following Perl
snippet are never seen by the user; they're silently
recorded in the installer's log. To watch the log as
a package is installed, select Show Log from the
Installer's File menu.

One of the things this
configure.pl Perl script does is call an
AppleScript script, configure.scpt, to prompt
the user through a series of dialog boxes for the answers to various
configuration questions and retrieve the responses as a string of
key/value pairs (stored in $options), later to be
applied automagically to the Blosxom application itself. Calling the
AppleScript script itself is just a matter of passing the script to
the osascript command-line utility that takes care
of actually running it for me:

Finally we come to the GUI bit itself. The
configure.scpt AppleScript consists of nothing
more than a series of display dialog statements,
each prompting for a single configuration setting and saving the
user's responses:

tell application "Finder"
activate
display dialog "You're mere moments away from your very own blog." & return
& return & "Before letting you get on with it, however, let's get a few last
settings taken care of for you." & return & return & "To skip this step,
click Cancel." & return with icon 1
display dialog "What would you like to call your blog?" default answer
"blosxom"
set blog_title to (text returned of result)
display dialog "How would you describe your blog (keep it short)?" default
answer "yet another blosxom blog"
set blog_description to (text returned of result)
display dialog "What language code would you like to associate with
your blog (e.g. en=English, fr=French, de=German)?" default answer "en"
set blog_language to (text returned of result)
display dialog "How many entries would you like displayed on your
blog's home page?" default answer "40"
set blog_num_entries to (text returned of result)
display dialog "What URL should be used for your blog (leave blank
to have Blosxom figure this out for itself)?" default answer ""
set blog_url to (text returned of result)
set result to ("$$$blog_title=" & blog_title & "$$$blog_description=" &
blog_description & "$$$blog_language=" & blog_language & "$$$num_entries="
& blog_num_entries & "$$$url=" & blog_url)
end tell

For instance, line 1 displays prompts for a blog name, as shown in
Figure 2-24, while line 2 saves the result to the
AppleScript variable blog_title.

Figure 2-24. One of a series of AppleScript dialogs

Line 3 strings all the various responses together to be returned to
the calling configure.pl Perl script.

While this actually does work remarkably well, it is overly involved
(albeit not particularly complex) for the rather simple requirements
it has to meet. Also, it has quite a staccato feel to it from the
user's point of view, with dialog boxes bouncing up
and down one after another. Unfortunately, there was just no way,
without doing quite a bit more programming, that I could get around
sending the user (very possibly a newbie) to the command line—a
no-no in my book.

I can now replace that staccato series of dialog boxes with a single
dialog box that sports all the configuration questions I
need—at least one window's worth.

Installation

Installing Pashua
is just a matter of downloading the disk image and copying the
contents across to your hard drive. To have the
Pashua.app itself readily accessible from
anywhere you care to use it, you might consider dropping it into your
Applications folder. You'll also want to make sure
that the associated Perl or Python module (Pashua.pm
and Pashua.py, respectively)
you'll be using in your code is somewhere Perl or
Python will expect to find it.

My preference is to keep all of the Pashua bits together, simply
creating a Pashuafolder in my
Documents/Code directory (where I do all of my
coding).

You just need to be sure that the Pashua.app
application and appropriate Pashua.pm or
Pashua.py module is available to your script.
The simplest way to do this is to copy these into the same folder as
your script and distribute the entire folder to anyone
who'll be using your software.

The Basics

Pasha
scripts are simply wrappers around a configuration string
that's fed to the Pashua application. This string
defines the dialog window itself and the various GUI widgets
you'd like Pashua to display.

While I'll not delve into every widget in turn, each
definition looks a little something like this:

title is the name I've given to
this particular widget; I could have used any string of letters or
numbers (e.g., title1, thingy,
or wotsit22). Each widget is described by using
attributes, prefixed with an underscore (_) and
appended to the widget's name. Most widgets take
four attributes: type
(textfield, button,
checkbox, etc.), label (what
appears alongside the checkbox or on the button),
width (in pixels), and a
default (which radio button is initially selected,
the initial value of a textfield, etc.). So, this
widget is a 200-pixel-wide textfield named
title, labeled What do you want to
callyour blog?, and with an initial
value of My Blosxom.

TIP:
It's all much like designing an HTML form, except
that attributes are listed as name_attribute, one
per line, rather than in an HTML tag. The just-described
textfield might look something like this in an
HTML form:

future_type=radiobutton
future_label=Should I show entries from the future?
future_option=No
future_option=Yes
future_default=No

This one is a radiobutton group named
future, labeled Should I showentries from the future?, with
No and Yes options, the former
selected by default.

As for the dialog window itself, there's not much to
fiddle with:

# Set window title
windowtitle=Blosxom Configuration Wizard
# Set transparency: 0 is transparent, 1 is opaque
transparency=0.95
# Set the window to brushed metal; the default is regular Aqua
appearance=metal

You'll find complete documentation for these
directives in the Pasha package itself
(Pashua/Documentation/documentation.html) and
various hints in the code and comments of the examples used in this
hack.

Simple Example

Let's start out with a simple example, one that
displays a single dialog window with some number of GUI widgets
representing the minimal configuration settings Blosxom requires.

The code

While I could have chosen any of the supported languages, I thought
I'd stick to Perl, because that's
what Blosxom is written in and I can write Perl in my sleep
(although, admittedly, I'm more of a Python devotee
at heart).

To a folder named Simple
(Documents/Code/Pashua/Simple), I copied
Pashua.app and Pashua.pm. I
then wrote the following script, borrowing the basic framework from
the example code (Pashua/Examples/example.pl)
found in the Pashua distribution. I saved the file as
Simple.pl:

# !/usr/bin/perl -w
# Simple.pl
# A simple Pashua example
# Add the local directory to the queue of places to look for Pashua.pm
BEGIN {
use File::Basename;
unshift @INC, dirname($0);
}
use strict;
use Pashua;
# Define what the dialog should be like
# Take a look at Pashua's Readme file for more info on the syntax
my $conf = <<EOCONF;
# Lines starting with a hash character are
# comments, empty lines are ignored
# Set transparency: 0 is transparent, 1 is opaque
transparency=0.95
# Set window title
windowtitle=Blosxom Configuration Wizard
txt_type=text
txt_text=Welcome to Blosxom.[return][return]You're mere moments
away from your very own blog.[return][return]This wizard will
take you through some last minute configuration settings. If
you're in need of some details, take a gander at the gory
details on the Blosxom Configuration page at
http://www.blosxom.com/documentation/users/configure/.
title_type=textfield
title_label=What do you want to call your blog?
title_width=200
title_default=My Blosxom
description_type=textfield
description_label=How would you describe your blog?
description_width=400
description_default=Yet another blosxom blog.
language_type=textfield
language_label=What will be your primary written language? (e.g. en=English)
language_width=25
language_default=en
future_type=radiobutton
future_label=Should I show entries from the future (i.e. post-dated entries)?
future_option=No
future_option=Yes
future_default=No
# Add a cancel button - if you like, you can set the
# button label by uncommenting the second line below
cncl_type=cancelbutton
#cncl_label=If you click here, no values will be returned
# A default button is added automatically - if you want to
# change the button title, you should uncomment the next
# two lines to override the "built-in" default button
#default_type=defaultbutton
#default_label=Click here to return the values
EOCONF
# Pass the configuration string to the Pashua module
my %result = Pashua::run($conf);
if (%result) {
print " Pashua returned the following hash keys and values:\n";
while (my($k, $v) = each(%result)) {
print " $k = $v\n";
}
}
else {
print " No result returned. Looks like
the 'Cancel' button has been pressed.\n";
}

Lines 7-12 make the Pashua.pm Perl module in the
current folder available to the script.

The heart of the script is a long configuration string that defines
the various aspects of the dialog window itself (lines 20-24) and
component GUI widgets (lines 26-59) stashed in a
$conf variable.

Line 63 is where the actual work gets done: the
$conf configuration string is passed to the
Pashua::run() method of the
Pashua.pm Perl module, which, in turn, asks the
Pashua.app application to display a dialog box
that contains the various described widgets.

Lines 65-73 print the results to the Terminal window, although
you'll most likely want to do something far more
useful with them in any application you're building
with Pashua.

Running the code

Run the Perl script on the command line, like so:

$ perl Simple.pl

You'll see the Pashua application bounce into being
in your Dock and your dialog pop up on the screen, as shown in Figure 2-25.

Figure 2-25. A simple Pashua-generated dialog

Click the OK button or just hit the Return key on your keyboard to
send the values of the various dialog widgets to the Terminal:

Hacking the hack

Since the lion's share of your script is Pashua
configuration, the process is just about identical regardless of the
language you choose. Here's the same thing (the
configuration is abbreviated) in Python:

Wizard Example

I created a new folder named
Wizard(Documents/Code/Pashua/Wizard) and
again copied Pashua.app and
Pashua.pm into it.

The code

The following script, Wizard.pl, defines two
separate dialog window configurations, $general
and $configurestatic, each to be fed serially to
the Pashua engine (additions and alterations from the code in the
preceding "Simple Example"
section—mostly the addition of a second screen
configuration—are called out in bold):

#!/usr/bin/perl -w
# Wizard.pl
# A multi-screen Pashua Wizard example
BEGIN {
use File::Basename;
unshift @INC, dirname($0);
}
use strict;
use Pashua;
# Define the Wizard's first screen
my $general = <<GENERAL;
# Lines starting with a hash character are
# comments, empty lines are ignored
# Set transparency: 0 is transparent, 1 is opaque
transparency=0.95
# Set window title
windowtitle=Blosxom Configuration Wizard: General
txt_type=text
txt_text=Welcome to Blosxom.[return][return]You're mere
moments away from your very own blog.[return][return]This
wizard will take you through some last minute configuration
settings. If you're in need of some details, take a gander
at the gory details on the Blosxom Configuration page at
http://www.blosxom.com/documentation/users/configure/.
title_type=textfield
title_label=What do you want to call your blog?
title_width=200
title_default=My Blosxom
description_type=textfield
description_label=How would you describe your blog?
description_width=400
description_default=Yet another blosxom blog.
language_type=textfield
language_label=What will be your primary written language? (e.g. en=English)
language_width=25
language_default=en
future_type=radiobutton
future_label=Should I show entries from the future (i.e. post-dated entries)?
future_option=No
future_option=Yes
future_default=No
cncl_type=cancelbutton
# A default button is added automatically - if you want to
# change the button title, you should uncomment the next
# two lines to override the "built-in" default button
default_type=defaultbutton
default_label=Next
GENERAL
# Define the Wizard's second screen
my $configurestatic = <<CONFIGURESTATIC;
windowtitle=Blosxom Configuration Wizard: Static Settings
staticdir_type=openbrowser
staticdir_label=Where would you like the static version of you blog to live?
staticdir_width=400
staticdir_default=/Library/WebServer/Documents
staticpwd_label=What would you like to use as your static rendering password?
staticpwd_type=password
staticpwd_width=200
staticpwd_default=
staticentries_type=radiobutton
staticentries_label=Would you like to statically render individual entries?
staticentries_option=No
staticentries_option=Yes
staticentries_default=No
editor_type=popup
editor_label=When would you like static rendering to run?
editor_width=200
editor_option=Manually
editor_option=Every 1/2 Hour
editor_option=Every 1 Hour
editor_option=Every 2 Hours
editor_option=Every 3 Hours
editor_option=Every 4 Hours
editor_option=Every 5 Hours
editor_option=Every 6 Hours
editor_option=Every 12 Hours
editor_option=Every 24 Hours
editor_default=Manually
cncl_type=cancelbutton
cncl_label=Cancel
default_type=defaultbutton
default_label=Next
CONFIGURESTATIC
# Define the rest of the wizard's screens
# ...
# Pass each configuration string in turn to the Pashua module to create
# a Wizard-like screen-by-screen interface and gather the results along
# the way
my %result = Pashua::run($general));
%result = (%result, Pashua::run($configurestatic));
# All the rest of the screens go here in the same manner
# %result = (%result, Pashua::run($shareware));
if (%result) {
print " Pashua returned the following hash keys and values:\n";
while (my($k, $v) = each(%result)) {
print " $k = $v\n";
}
}
else {
print " No result returned. Looks like the
'Cancel' button has been pressed.\n";
}

Line 104 runs Pashua::run(), feeding it the first
screen configuration held in the $general
variable. Line 181 calls Pashua::run() again,
this time feeding it the second screen, as described in
$configurestatic.

Running the code

Run the Perl script on the command line, like so:

$ perl Wizard.pl

Up comes the first screen of your wizard. Click the Next button to
move on to the next screen, shown in Figure 2-26.

Figure 2-26. The second screen of a Pashua-generated wizard

Click the Next button again (in your final wizard, a Finish button is
probably more appropriate for the last screen) or just hit the Return
key on your keyboard to send the values of the various dialog widgets
across all the wizard's screens to the Terminal:

WARNING
Be sure to choose different names for your various variables across
wizard screens, because all the key/value pairs are stored in a
single hash (at least in this example) and a second widget of the
same name on another screen will overwrite the value of the first.

And this gets me to right where I wanted to be: a multiscreen
configuration wizard that's capable of being called
as a script by the Mac OS X Installer.

But if you try this yourself, you'll notice
something slightly irritating: for each screen, the Pashua app is
brought to life and killed off, over and over. While not quite as
staccato as the grotty way I started out with and far more
feature-filled and flexible, it still feels a mite bit rough around
the edges. And what if the user wanted to rerun it?
She'd have to visit the command line and invoke it
from there—still a no-no.

Double-Clickable Example

Wouldn't it be nice to make a self-contained,
double-clickable application of this, turning your simple Unix script
into a GUI wizard that's virtually indistinguishable
(except for the more limited form-driven-only functionality) from a
first-class application?

The Pashua distribution includes just such a thing in the form of
Doubleclickable Example
(Pashua/Examples/Doubleclickable
Example.app).

Option-drag a copy of Doubleclickable Example
somewhere for editing (leaving the original as it is, in
case you need it again as a starting point); my copy is
Documents/Code/Pashua/Doubleclickable Example.

Control- or right-click the application and select Show Package
Contents from the context menu, as shown in Figure 2-27. Browse through the app until you get to
Doubleclickable Example/Contents/MacOS. Notice
that both the Pashua app and Pashua.pm
Perl module are baked right into the
application's contents so that
they're readily available to the core script. The
script itself has the same name as the outer application,
Doubleclickable Example.

The code

Here's what is essentially the same code as appeared
in the preceding "Wizard Example"
section. I've added in a few more screens and put in
some basic logic for which screens to show, given the input provided
on the previous screen and so forth:

There are some slight changes—aside from the whole thing
running as a first-class, double-clickable application, that
is—worth pointing out here.

Line 2 is a cosmetic change, replacing the Next button of the
previous screens with a more appropriate Finish button.

Lines 3 through 12 call the Pashua::run() method
for each screen and store the results in a %result
hash. I have added some logic to check after each screen that the
user isn't trying to get out of the wizard by
hitting the Cancel button (Lines 5, 7, 9, and 11). If so, we
terminate the script on the spot.

Now, of course, we could simply have dropped all the screen names
into a loop and iterated over them one by one, like so:

Line 8 (requiring an exception) is why I chose to do things manually.
We offer the user a choice of configuring Blosxom for dynamic or
static rendering. Since only the latter requires any kind of
additional configuration, the $configurestatic
screen is called only if the user selects the Static radio button in
Line 1.

The last few lines of the script do something useful with the
results—or, in this example, simply stash them somewhere for
now. You can't just print the values out to the
Terminal (as in the case in the Simple or Wizard examples), because
this script runs as a double-clickable app and
doesn't involve a visit to the Terminal at all.

Running the code

Double-click the Doubleclickable Example
application icon in the Finder to open your dialog window. Click the
Next button to jump from screen to screen. Be sure to select Static
mode on the Dynamic or Static screen; otherwise,
you'll never get to see the Static Settings (as well
you shouldn't) shown in Figure 2-28. At the end, click the Finish button to finish
up. You can quit the app at any time by clicking the Cancel button or
hitting the Esc key on your keyboard.

Hacking the hack

While the documentation says that to change the name of the app you
need to change both the name of the outer application,
Doubleclickable Example.app, and inner
Doubleclickable Example Perl script, I found
that changing the name of the outer without touching the inner worked
like a charm; indeed, changing the inner script's
name caused the app not to run at all.

iOscillate

Get in enough face time with your fans by means
of an iSight, an oscillating fan, a little ingenuity, and a
well-developed sense of play.

If you've ever actually tried to do any
video-conferencing using iChat and an
iSight (or equivalent camera [Hack #34] ), you've no
doubt found that it works surprisingly well. Sure, there are sound
hiccups and video burps, but most of these can be ameliorated. Add a
tad more bandwidth (e.g., DSL instead of 56K modem dialup).
Don't download large MP3 files during the call.
Shutting off email stems the tide of those large attachments washing
in from the office. Or simply use an actual telephone (gasp!) for
audio.

But try it with a roomful of people spread unevenly around a
conference table and you're sure to find yourself
staring at a stray notepad, box of tissues, hopelessly out-of-date
organizational chart, or the one person in the room not saying a
thing or moving a muscle. Now, you'd think some
kind-hearted soul would move the camera every so often, pointing it
at least at another unmoving, unblinking participant or different
notepad; they probably won't. You'd
hope someone would be nominated to or just take charge of pointing
the camera at whoever is speaking; it doesn't
usually happen. Even when talking directly to the poor schlub on the
far end of the call, people will actually stare at the side of the
camera, as if doing so somehow provided more presence.

So, what's a telecommuter with poor iSight to do?
Why, oscillate, of course.

An iSight
mounted to the top of debladed oscillating fan, as shown in Figure 5-50, sweeps out up to a 180-degree field of view.
While this doesn't mean you're
necessarily going to be looking at the person speaking for more than
a split second or so, it does provide more of a sense of actually
being there—albeit in an admittedly nauseating fashion.

Figure 5-50. iSight + oscillating fan = iOscillate

Intrigued? I was too when the idea first struck, so I set about
building one.

Building an iOscillate

Throwing together an iOscillate of your very own is trivial, eating
up a scant 15 minutes or so. It requires little in the way of parts,
and no tools are necessary.

Prepare the fan

Appropriate a disused or otherwise available oscillating
fan—probably not best done on the hottest day of the summer.

Strip it of its blade and metal or plastic cage. Your average Walmart
unit ships with these parts preremoved for your convenience. If
it's already assembled, disassembly usually entails
only two or three steps and requires no tools. Unclip the cage edges
to separate the front portion. Unscrew the nose (usually clockwise in
the U.S.) that holds the blade in place and remove the blade. Unscrew
the washer that holds the back portion of the cage in place and
remove it. If possible, replace the nose so that the spinning metal
shaft doesn't hurt anyone.

Mount the camera

Attach one of the various plastic connectoids that came with your
iSight or other webcam to the top of the fan. I found that the flat,
sticky-based iMac mount worked nicely with my iSight.

You can even just use Scotch or duct tape if all else fails. This,
however, does mean that it'll be difficult to
impossible to point the camera up or down as needed to catch the
faces rather than ties or toupees of the participants.

Try to keep the camera itself away from the fan, to avoid vibration
and cut down on the noise of the motor (if your webcam has a built-in
microphone).

Do make sure that the camera is upright. And whatever you do,
don't even think of strapping it to the
soon-to-be-spinning metal shaft.

Run the USB or FireWire cable

Drape or stick down the webcam's USB or FireWire
cable in such a way that it has more than enough play yet is well
clear of the metal shaft that is used to turn the blade, the
mechanics involved in oscillation of the fan head, and anything else
electro-mechanical on the fan.

Oscillating

Place this contraption on a conference table, such that it is most
likely to provide a sweeping view of all participants—not to
mention the occasional glimpse of that gorgeous oak tree outside the
window. This generally works best with all participants arranged in
an arc slightly shorter than 180 degrees, close enough together so
that the camera doesn't try to focus on the wall
behind when there's a wide enough gap between two
people.

Hook the iOscillate up to a Mac (or PC if compatible), orienting the
screen so that most of the participants can see the person on the
other end of the line.

Start your engines!—or sufficiently quiet, steady, and
well-geared motor. While the speed setting you choose should have no
bearing on oscillation, I did find that my fan's
High setting made for a smoother ride.

Fire up iChat or the equivalent and ring your remote peer.

You might suggest he pop some Dramamine or wear those oddly effective
seasickness wristbands. These things do whiz along at quite a clip
and the camera can sometimes get confused while trying to maintain
focus.

Hacking the Hack

Picking your oscillating fan is key. While any old fan will do, if
you're going to go out and buy one—really you
shouldn't, not unless you're hot,
that is—you might see if you can find one with an adjustable
oscillation speed. Also, pay attention to the vibration-to-dollar
ratio of some of the cheapest models.

A friend suggested actually leaving the fan blade and cage assembly
intact, so as to actually cool the participants and make for a nice,
wind-blown supermodel effect. If you mount the webcam behind the
cage, know that the blades will confuse the iSight's
autofocus to no end. If you mount it to the top,
you'll find it vibrates considerably and
there's a risk of catching some part of the USB or
FireWire cable in the blade.

For a decidedly manual version of this hack, try placing your webcam
on a lazy Susan: that revolving tray one finds in the center of large
round dining tables. You'll still have to remember
to aim the camera at whoever is talking, but it then becomes a group
endeavor (and makes for a smoother ride than the usual jiggly
reorientation). Place the laptop that's hosting the
session next to the camera so that participants can see to whom they
are speaking. Put the speakerphone on the tray too for greater sound
quality on the listener's end. Or, if
it's a lunch meeting, use it as intended: to pass
the Kung Pao and rice.