Menu

Scripting Collaborative Applications with Flash Communication Server MX

August 2, 2002

Jon Udell

When Macromedia's new Flash Communication Server MX hit the streets last month, its streaming media features were the focus of attention, and rightly so. Injecting a Webcam widget into a Flash application--one that you can share with anyone else who hits the Web page that embeds that Flash app--takes literally a few seconds and no lines of code. With a bit of server-side scripting, video feeds can be captured and played back on demand.

As David Doull said in his Thinking in Flash Weblog, "video and audio chatting is something different," as is the asynchronous audio/video messaging this technology enables. By way of example, Doull points to Dream Domain, where you can pilot an insect drone (which, if you like, you can genetically mutate) through a landscape of virtual rooms, each dotted with text and Webcam messages dropped by visitors.

Of course, for the white male hackers who mainly visit the site, the video messages found there are, predictably, glimpses into the mirror: white male hackers smoking cigarettes, pretending to be attacked by aliens, pressing eyeballs close to the lens. Still, as bandwidth grows and AV-equipped PCs become the rule rather than the exception, there is no question that audio and video will augment text as first-class datatypes, routinely created, exchanged, and used by everyone. At first this will mean more silly home movie-lets. Eventually, however, we'll learn how to use AV clips appropriately, to enhance business and social communication. When we look back, we'll remember that FlashComm server helped make that vision real.

But coupled with Flash MX, this product adds up to much more than an AV toolkit. FlashComm's RTMP (Real-Time Messaging Protocol) isn't just for streaming media. It supports pub/sub event distribution networks that can be wired up one to one or one to many. These events can be produced by AV streams, but also by any of the GUI widgets in Flash's arsenal. What you build with FlashComm are applications whose interfaces are shared in real-time--or, thanks to built-in persistence, asynchronously--across networked PCs among groups of people. Presence, the killer feature of IM, is woven into the fabric. Client-side and server-side code share common APIs and, crucially, both use ECMAScript (more popularly, JavaScript) to achieve a level of rapid-development productivity that Jabber (for example) lacks.

Unlike Jabber, the FlashComm experience doesn't come free. The server, which runs on Windows (soon Linux and Solaris as well), is $500 for the 10-users personal edition, and $4,500 for the 500-users professional edition. Add $500 for the Flash MX development kit, and that's $1,000 to get your feet wet, $5,000 for anything serious. You can try both for 30 days, but the ante's a bit steep for longer term experimentation. For certain corporate and educational customers that won't be a problem. And for users of FlashComm applications, the Flash 6 player is free and on a fast track to ubiquity. There are also more affordable hosting options offered by, for example, mediatemple.net. But in general, FlashComm's price tag will limit the number of developers who explore its distributed-event style of Internet programming, as will the so-far-proprietary nature of RTMP.

Other commercial and proprietary ventures are headed down similar paths. Groove's shared spaces, KnowNow's event-routing technology, and Kenamea's application network are notable examples. I regard these, and now FlashComm too, as infrastructure that complements the Web services movement. So far, the Web services plumbing is mainly about connecting services to services, machines to machines. Evolving in parallel are new ways to connect services to people, and people to one another. Plenty of this is also happening at the grassroots level, as for example Weblog developers push the envelope with innovative hacks like TrackBack and MT-RefSearch. I can't say whether and how FlashComm (or the technology inspired by it) will intersect with the grassroots Internet. I am certain, though, that the ideas it embodies will change how we think about, and create, collaborative software. So, let's explore how it works.

FlashComm 101

The FlashComm server, available for Windows (and soon, Linux and Solaris) is a compact, easy-to-install engine with a Web-based administrative interface that (of course) uses Flash. To jump-start things, you'll want to install the FlashComm components as well. These are paired client-side and server-side objects. The client parts are movie clips sourced by Flash MX from a local components.fla file into an application's .fla file, and thence into the published .swf application. The server parts, which are just ECMAscript files, live on the server under /flashcom/scriptlib/.

An application's .html and .swf files are published to a Web-visible directory under, for example, /wwwroot/flashcom/applications. Each application also has a parallel and non-Web-visible directory under /flashcom/applications/, where persistent objects (if the application creates them) are stored.

In addition to the components, you'll want to download and install the Flash remoting components if you don't already have them. These are mainly used with ColdFusion MX and not required for FlashComm to work, but the included NetConnection debugger is a handy tool to have. Without this kit, you'll get errors when you try to #include "NetDebug.as" as the documentation suggests. As Mike Chambers, Macromedia's Flash community manager, pointed out to me, the server can, in any case, access remote Web services using the same NetServices APIs built into ColdFusion MX's Flash remoting gateway.

Here's how easy things can be using the FlashComm components. I began by dragging a SimpleConnect component onto the Flash MX stage. SimpleConnect combines a login widget with a wrapper that binds the user identity it creates to any of the other widgets. Then I dragged more components onto the stage: PeopleList, Chat, UserColor, and AVPresence. I assigned instance names to each of these, and in SimpleConnect's properties pane, I made two entries:


Application Directory      rtmp://myserver/mychat

Communication Components   [people_mc,chat_mc,colors_mc,av_mc]

Then I loaded up http://myserver/flashcom/applications/mychat/mychat.html from three different computers. In each instance, I logged in as a different person, selected a color for each identity, and typed a chat message. It was a fully functional chat, with presence shown as a changing list of names in the PeopleList widget, and a persistent transcript. When I activated the AV stream on my Webcam-equipped PC, it appeared on all three stations. This is stunning leverage, on the order of what Hypercard and then Visual Basic 1.0 achieved.

What's equally impressive is the transparency of the whole system. Hypercard XFCNs and Visual Basic VBXs, the component technologies of that era, were opaque to the scripter. With FlashComm, both client-side and server-side components are themselves made of script, and thus fully extensible. To find out how, I decided to add a few features. First, I added a button to the application, which enables the user to clear the chat transcript. This is a feature implemented in the server part of the chat component. A few lines of ECMAScript empty the persistence-backed shared object that remembers the transcript on the server, then broadcasts a message to all clients telling them to empty their transient local transcripts.

Extending a Communication Component

On the first try I dragged a pushbutton onto the stage, called it ClearHistory, and wired it to this function. I added to the Actions for Frame 1 of the movie:


function onClearHistory()

    {

    client_nc = new NetConnection();

    client_nc.connect("rtmp://myserver/mychat");

    client_nc.call("FCChat.chat_mc.clearHistory");

    }

This worked, but it was the wrong approach for lots of reasons. The function should be part of a modified Chat component, not wired to a stand-alone button. It unnecessarily opened an extra connection to the server, instead of sharing the one that Chat and the other widgets were multiplexing through SimpleConnect. And it needlessly hardcoded the instance name of the Chat component.

The right approach, I concluded, was to clone the Chat component and add a ClearHistory button to it. Since I'm a complete Flash novice, I admit I fumbled around quite a bit trying to figure out how. Thanks to some helpful guidance from Mike Chambers I muddled through. (It helps if you unlock the widgets you're trying to edit!) Once I opened up the Chat component, I saw that it already had the needed function, along with a namespace prefix:

FCChatClass.prototype.clearHistory = function() {
    this.nc.call( this.prefix + "clearHistory", null );
}

All I had to do was add a ClearHistory button to the Chat component, and name its click handler clearHistory.

The next idea was to add a label called clearedBy to the Chat component, in order to record who last cleared the transcript. There are probably a dozen ways to do this. The one I came up with uses a transient shared object as a messaging channel between the server and all the connected clients.

On the client side, in the Chat component's connect() method, I added:


this.clearedBy_so =                                

// create the shared object

   SharedObject.getRemote(this.prefix + "clearedBy",

        this.nc.uri,false);



this.clearedBy_so.clearHistory = function  (who)   

// declare callback function

  { gChat.clearedBy.text = "by: " + who; }



this.clearedBy_so.connect(this.nc);                

// connect the object

The first client to call SharedObject.getRemote creates an object on the server which, in this case, ends up being called FCChat.chat_mc.clearedBy. (If the third argument to SharedObject.getRemote is true, the object persists to the file /flashcom/applications/mychat/sharedobjects/_definst_/fcchat.chat_mc.clear edBy.) Subsequent clients connect to that same object.

In the server-side Chat component's constructor, I connected it to the clearedBy_so shared object like so:


this.clearedBy_so = SharedObject.get( this.prefix + 

     "clearedBy", false );

Then, in the server-side Chat component's clearHistory method--the one invoked remotely by the function I'd wired to the ClearHistory button--I added:


user = this.getClientGlobalStorage(client).username;

this.clearedBy_so.send( "clearHistory", 

     "[" + username + "]" );

This code unpacks the username from the per-client data managed by the server, and passes it to the clearHistory function attached to the client-side clearedBy_so shared object, which calls the local function that assigns the passed username to the clearedBy label.

Synchronized Persistent Objects

The PeopleList component is built using a transient shared object. That's appropriate for a real-time presence indicator, but suppose you also want to log and count the users of your chat application. Exploring this idea gave me a chance to try out persistent shared objects. I started by dragging a ListBox--an ordinary Flash UI component, not a special Communication Component--onto the Chat stage.

Here's the client-side code to create a persistent shared object in which to remember all the users who have signed in.


users_so = SharedObject.getRemote(this.prefix + 

     "users", this.nc.uri, true);

users_so.connect(this.nc);

On the server, as before, I mirrored this connection in the Chat constructor:


this.users_so = SharedObject.get( this.prefix + 

     "users", true );

In the server's connect() method, I added:


this.users = this.users_so.getProperty("users"); 

// unpack the shared object



if ( this.users == null )

    this.users = new Array;



found = 0;



for (i in this.users)                            

// look for our user

    {

    if  ( this.users[i][0] == cglobal.username ) 

    // if found

        {

        this.users[i][1]++;                      

        // increment visit count

        found = 1;

        }

    }



if (! found )                                    

// otherwise

    { this.users.push([cglobal.username,1]); }   

	// record first visit



this.users_so.setProperty("users", this.users);  

// save shared object

All clients connected to this shared object automatically receive a synchronization message when its data changes. Back on the client side, here's the handler I wrote for that synch message:


users_so.onSync = function (list)

    {

    users_mc.removeAll();              

	// clear the ListBox

    for (i in users_so.data.users)     

	// and refill it

        {

        users_mc.addItem (

            users_so.data.users[i][0] + " (" +

            users_so.data.users[i][1] + ")" );

        }

   };

The result is a listbox, on each client, that counts visitors and visits like so:


Jon (3)

Doug (4)

Gus (1)

Something Different, Something New

The demo applications included with FlashComm do a good job of sketching further possibilities. I particularly like the presentation example. A speaker sends an AV stream to an audience, along with a slide show (which is itself an embedded Flash movie). As the speaker advances through the slides, the audience stations are, by default, synched. However, they can optionally decouple and view slides independently. Audience members can ask questions using chat, or by sending an AV stream to the speaker. It doesn't take much imagination to spin out lots of useful variations on this theme.

Flash MX and the FlashComm server together deliver event-driven peer networking, streaming-media services, a productive scripting environment that targets networked teams of people, and powerful components that embody the essential tools of collaboration. We've seen all these ingredients before, but Macromedia has combined them to create something different and new: a killer framework for the rapid development of collaborative software.

Editor's Note: For many more details on client-side ActionScript programming, see ActionScript: The Definitive Guide. The first edition covers Flash 5, but 99 percent of it still applies to Flash MX. The revised Flash MX edition is due out in the fourth quarter of 2002. For anyone wanting to learn the JavaScript syntax for server-side programming, check out JavaScript: The Definitive Guide, 4th Edition.