Tenfold: Celebrating 10 Years of Ink & Switch
Livelymerge Notes

A Morphic programming kernel

Dan Ingalls, Alex Warth, and Peter van Hardenberg

bugs

Introduction

A number of conversations have circled around a kernel to facilitate experiments with live programming in the context of Patchwork; a kernel with somewhat the flavor of Squeak, Forth or Lisp. If sufficiently simple and malleable, it should enable exploration within Patchwork, multi-user experiments, programming on tablets, and even debugging using our own tools (which can be customized) instead of the Chrome debugger. This note is about the kernel itself, apart from these other explorations.

A programming kernel is a body of software capable of supporting the development of useful applications. As in biology, the term "kernel” connotes being small; yet it is a seed that carries with it all the essentials for growing into a full-fledged something.

Simplicity and generality are the hallmarks of good software kernels. Simplicity almost by definition implies compactness and ease of understanding. Generality implies that this kernel can build or grow into a wide variety of useful tools.

A special category of kernels comprises those that are metacircular, i.e., those that can be used to alter and improve themselves. With such a kernel in your toolbox, you need only port that one piece of software to bring it and all the applications it has built to a new environment.

The concept of a kernel is a solipsistic one. To be practical, it must be described in a programming language, and installed in a hardware environment that provides the means of program execution and facilities for useful input from and output to the external world.

In this note, we will discuss a programming kernel written in an object-oriented dialect of JavaScript and coupled to the input and output facilities common in web browsers of today. Being metacircular, this kernel is inherently malleable, and should lend itself to exploration of programming in the context of Ink & Switch software.

Here we discuss deployment with a graphical user interface provided by an HTML canvas inside Patchwork. The system is small enough to provide an interactive programming environment that runs in any web browser, with adaptation of its object memory to support multi-user experiments, and some affordances for use on tablets.

How we built the kernel

From previous experience with programming kernels, we chose to proceed along a path of motivational reinforcement. First and foremost, we aimed for “bits on the screen” in the form of a simple ellipse shown on our canvas.

We grew a little framework around this goal in the form of a window object with an array of children of which our ellipse could be one. We added plumbing to deliver pointer events from the web browser, and soon we were able to drag the ellipse on the screen by using the mouse on a laptop or a finger on a tablet.

Now in possession of this trivial graphical situation, we built it out by defining some basic object-oriented abstractions in the form of classes to describe Points and Rectangles, the basic arithmetic of a graphical world. This made it simple to write a little bounce routine with JavaScript’s setInterval driving our ellipse back and forth across the screen. Our little kernel began to feel lively.

The next dimension of growth for our little world was to harness the various shapes supported by the HTML canvas. This includes rectangles, ellipses, and polylines (simple lines, multisegment lines, and curves). We always include a Pen class for the creation of poly lines. Instances of this class support the basic methods go(distance) and turn(angle) of turtle geometry. Having such a pen makes it easy to describe stars and spirals to test the polyline code, and have a little fun, too.

Then there’s text. Yes, text. For a system to be self-supporting, it must be able to edit code. While the canvas provides basic functionality for layout of characters (via CanvasRenderingContext2D’s measureText and fillText methods), several embellishments are necessary to make a useful object. First, you need multiline composition. For a motivating first pass at this, we simply broke the string at new-line characters. Then, we added support for selections, which amounts to implementing two methods: charBlock(index) and charBlock(point). Each of these methods returns an instance of TextCharSpec, which includes a character’s string index, line number, and its x- and y-coordinates. Once you have selection, the operations of cut, paste and type-in are simple string replacements. We say “simple” because in the birth of this kernel, we made almost every decision in favor of simplicity rather than, say, performance. One departure from this approach was to import a full bracket-matching function which I (Dan) had written earlier for Squeak and the Lively Kernel.

The Morphic Scene Graph

We chose to implement a Morphic graphics model. Morphic is simple to implement, flexible to use, and it has seen decades of service in Self, Squeak, and Lively.

All objects (morphs) on the screen live in a hierarchy, similar to the parent/children structure of HTML. Each morph has at least the following properties:

All morphs also have a render() method that renders its shape and any submorphs. Every morph also has a 2D coordinate transform between its bounds (in its owner’s coordinate system) and its shape and any submorphs. Most application components are implemented as subclasses of Morph.

In this way, the tree of morphs represents an entire scene of visible active objects.

Usually a morph renders its shape first (and therefore “behind”) as a background, followed by any submorphs, which appear in front. Submorphs are rendered in the order they appear in their owner’s submorphs array; thus the first submorph appears “behind” the later ones.

The root of a Morphic scene graph is an instance of WorldMorph, typically referred to as the world, and accessible to all morphs in the scene graph by the expression this.world().

The WorldMorph is similar in structure to any other morph, but it is in charge of distributing pointer events throughout its tree of submorphs that represents the entire scene. Unlike the procedure of rendering, the distribution of pointer events goes in the opposite order so that things in front get “first dibs”. The world morph also provides the service of running “ticking scripts” on various morphs to update their appearance and animate them in other ways.

Simple Morphs and Halos

rotating a morph via its halo

Our system supports the basic graphic shapes: Point, Rectangle, Pen, PolyLine, Image, and TextBox. These in turn participate in the Morphic scene graph through the classes Morph, LineMorph, ImageMorph, and TextMorph. Most of the user interface is made up of Panels with Panes (TextPanes and ListPanes) and TitleBars. The panes window their content and provide a menu button and scrollbar for their content.

Halos provide ten handles for manipulating morphs. Halos are accessed by a meta-click on any morph. Repeated meta-clicks on the same morph will traverse the owner chain up as far as the world, after which the sequence repeats. The halo’s handles offer the following functions:

Text Editing

Critical to the task of programming, and to most information displays, is the ability to present and edit text. Text editing in this system is very simple - there are no automatic pop-ups or type-aheads. The following command keys provide basic edits:

Note that double-clicking next to most bracket characters will select matching parentheses and other brackets (even // and /*). A double-click at the beginning or end of the entire text will select all of it. If you shift-click near either end of a selection, it lets you draw out that end of the selection range.

Most of these functions are also available in a fleeting menu beside list panes and text panes. This reduces the need for meta keys on tablets.

Compound Morphs and the Programming Interface

Programming tools are built up from these simple graphics in a number of PanelMorphs with ScrollPanes. These provide scrolling lists and text from which are made such tools as class browsers, method lists and inspectors. Panels retain the same proportional layout of their panes when resized by a halo’s Z handle.

Each of these panels is endowed with a title bar which serves as a handle for moving it around the screen, as well as two buttons for collapsing or deleting the window. When a panel is collapsed, its title remains visible, and both its expanded and collapsed locations are preserved. The world morph is able to bring active panels “to the top”, simply by changing their order in its submorphs array.

PanelMorphs are expected to house one or more ScrollPanes. The ScrollPane class provides for scrolling and clipping of text and lists. Scroll panes may offer a scroll bar to control scrolling of their content, and a menu button to provide access to particulars of the pane’s content including its selection if it has one.

Here a PanelMorph, two ListPanes and a TextPane comprise the workhorse system browser for this little kernel.

panes, etc.

Programming Tools

System Browser

The system browser is the workhorse of code navigation and editing in the system, shown above to illustrate the structure of most paned windows. It can be opened from the world menu, or using the B handle of a halo in which case it will open to the class of the halo’s target object.

Search Results

search results

The search results panel presents the result of a find command in any browser or other piece of text. Whether you are new to the system or an old hand, the find command will be your most valuable tool. You need only double-click on a method name followed by ctrl-F to see a list of all senders and implementers of that method. When you select one of those methods, it will highlight first the occurrence, and subsequent occurrences can be scanned with ctrl-G.

Inspector

inspecting a star

The inspector is valuable for examining Morphic structure and the dynamic state of each object. It can be opened by the expression something.inspect() or by using the I handle of an object’s halo.

The inspector lets you view the parts of any object. It also includes an evaluation pane where you can execute JavaScript expressions with this bound to the morph that’s being inspected. In the screen shot above, this facility is being used to make the star start spinning (see the section on stepping, below).

Style Panel

Clicking the S in a morph’s halo will bring up a style panel, a rudimentary tool for controlling the morph’s fill color, border color, and border width. All the controls are live; the revert and save buttons give final control over the changes that you make.

Recent Changes

The world menu item “Recent Changes” will open a method list panel of recently added or changed methods. This is handy if you wonder when someone made a certain change, or what that method looked like before. By browsing to old versions of a method and using the editor’s save command, you can revert to any of those previous definitions.

Method Browser

A full panel devoted to a single method. Use the spawn command in the system browser to see several methods without needing full system browsers for each.

Console Panel

A scrolling transcript of the JavaScript console.

Other features from the world menu

world menu

Stepping

stepping

This Morphic system provides a stepping mechanism for various animated effects in the user interface and in built applications. The world morph supports this by maintaining a list of SteppingSpecs, where each spec identifies:

You can see an example of this in the video above. The expression this.startStepping(20, 'rotateBy', 0.05) creates a SteppingSpec that sends the star a rotateBy message with an argument of 0.05 radians every 20 milliseconds.

Note that, with the star’s transform selected, you see its value changing as the star spins. This nice behavior of the inspector is provided by a similar stepping method!

Multi-user details: Hands, ephemeral objects and messages

Users collaborating on the same Livelymerge document can see each other’s hands, each of which has a different color. If your hand is green, you will notice that when you pick up a morph, its drop shadow will have a green tint to it. I’ll see that too – it’s how I know that you have picked it up. Similarly, if you are editing text, you will notice that its “dirty border” is green. We are currently experimenting with these mechanisms to enable users see what each other is doing, which helps avoid conflicts.