A live JavaScript environment

✒️ DescriptionA live programming environment for JavaScript inspired by the Self language. It allows us to directly manipulate objects and their connections.
▶️ Try It OutLive Demo
⚡ TechnologiesTypeScript, HTML, CSS, JavaScript
🗂️ Source CodeGitHub Repository

Static definitions vs. dynamic objects

While studying object-oriented programming, I’ve frequently come across discussions about the pervasiveness of a “class-oriented” style: how our focus is on classes instead of objects while we are programming.

We can conjecture that one reason for this is that many languages provide syntax for classes, but not for objects. For example, if we program in Java, we’re always “writing classes”; the objects —i.e., the instances of those classes— exist only when the program is running.

Most of those discussions are centered on the effects of those ways of thinking on software design, generally associating the “class-oriented” style with bad design. However, I’m more interested in thinking about that discussion from another perspective, analyzing the separation of a static “coding time” and a dynamic “run time”, which are mutually exclusive. Let me explain.

Clarifying the vocabulary

Going back to our conjecture, we could then say that languages providing “syntax for objects” will allow for “truly” object-oriented programming (as opposed to class-oriented programming). One example could be JavaScript, where we do have syntax for objects, not just classes:

javascript
const anObject = {
    name: "Pepe",
    sayHi() {
        return `Hi, I'm ${this.name}`
    }
};

However, the validity of what we’re saying depends on what we mean when we say “object”. If we dig deeper, we could say that what’s shown in the code is not really an object; that’s an object literal, which represents a static definition of an object but not the object itself (which will exist only when the program runs).

To make this distinction clearer, consider the following example:

javascript
function personNamed(name) {
    const anObject = {
        name,
        sayHi() {
            return `Hi, I'm ${this.name}`
        }
    };
    
    return anObject;
}

const pepe = personNamed("Pepe");
const marta = personNamed("Marta");

In this case, there’s only one (1) object literal, but there will be two (2) objects created from it when we run the program. So a static definition used to create objects (i.e., the object literal) is not the same as the objects themselves.

The same thing happens with classes, especially if we consider that in many languages classes are also objects![1] This distinction lets us refine the previous discussion, allowing us to frame it differently and be more nuanced about it.

The direct manipulation of objects

So, if we use the word object to describe the dynamic runtime entities, then the only way a programming system can let you manipulate objects while you program is if it does not make a separation between a static “coding time” and the system’s dynamic runtime.

In other words, the system should not force us to work in two separate stages:

  1. Changing the static definitions in the program[2].
  2. Running the program and seeing the objects, without being able to change them.

This is because, if we want to be able to directly manipulate and modify the objects, then we need the capabilities of (1) but while we are in (2), where the objects exist.

Some programming environments, such as the Smalltalk language, operate exactly in this manner. To describe the kind of direct access to the objects given by the live, integrated environment of Smalltalk, I like the following quote from Kent Beck[3]:

In Smalltalk, the objects are right there, an inch under the glass. You don’t have a text editor that works on a stream of characters that’s going to get interpreted, that’s going to eventually turn into objects; you are working with the objects. You point at them, you click at them, you edit them; they are right there. You have no choice but to interact with the objects.

This can also be experienced in a stronger way while using the Self language, a direct descendant of Smalltalk. In Self, you can see the objects and their connection to other objects, grab them, move them, and also evaluate code in their context:

A screenshot of the Self programming environment, showing three outliners: one displays a 'point' object with slots for x=1, y=2, and its parent slot; another for the 'traits point' object, with a parent slot and a 'plus' method; and the last one for the object '2'. The first outliner has visible lines connecting its parent slot to the second outliner, and the 'y' slot with the third outliner. Each outliner also has a text box with three buttons underneath: 'Get it', 'Do it' and 'Close'.

In a way, Smalltalk and Self put the objects right in front of you, reducing the distance between you —the programmer— and the running system: working with objects by directly “poking” at them on the screen highly contrasts with the more traditional process of modifying a text file → compiling → running, that inserts layers of indirection between you and the running program.

An experiment with JavaScript

JavaScript is a widely used language that was in part inspired by Self, sharing many of its characteristics (such as being dynamically-typed and prototype-based). This connection made me wonder if we could experience JavaScript in an environment that offers the kind of direct object manipulation that made Self so unique. So, I started working on an experiment to make it happen.

Using TypeScript and the web browser’s DOM, I developed an environment to visualize and manipulate JavaScript objects in real-time. The code is available on GitHub, and you can experience it firsthand here.

The environment provides several features that embody the direct object manipulation style of Self. For example, you can visualize objects with their properties and connections to other objects, making the object graph tangible and explorable:

A visualization of a JavaScript 'point' object with properties _x:1 and _y:2, showing connections with its prototype and the number 2, which are also being visualized. The interface mimics Self's outliner style but uses JavaScript terminology, with a prototype slot instead of a parent slot, and a text field with 'Do it' and 'Get it' buttons below.

Properties can be rewired in real-time by dragging the visible connection that connects them with their value: Animation showing the following: an outliner for the 'point' object is displayed, its _x property being connected to a number value '1'. There's also an outliner of another number value '2' below. The user grabs the arrow point with the cursor, and drops it into the outliner for the number 2. As a consequence, the value of the _x property of the point is changed to 2, and the arrow stays pointing to '2' instead of '1', demonstrating the ability to modify property references in real-time.

Code can be evaluated in the context of any object, and its result can be inspected:

An animation showing the following interaction: in the outliner for the point object, input 'this.plus(this)' and Get it; a new point object is displayed; then input "this._x = 123" and Do it; the value of the _x property of the point is changed to 123.

There are many other features of the system you can find by freely exploring it. An interesting one are the object and property attributes indicators, you can see them if you freeze an object by running Object.freeze(this).

The system was programmed using TypeScript and the web browser’s DOM. All the behavior was developed using Test-Driven Development, including comprehensive testing of view interactions (which is very uncommon). A special effort was done to be able to run the system in mobile devices with multitouch interfaces.

Conclusions

While developing this environment I learned a lot of things about both JavaScript and live programming tools. Although there are many missing features, I feel it does let you experience some of the direct manipulation affordances given by the dynamic systems that inspired me, where the only thing between you and the objects is the glass of the screen.

This experiment also revealed both challenges and opportunities in modern programming environments: while today’s development tools often maintain a strict separation between coding and runtime, alternative approaches are possible even in contemporary, mainstream languages. As we continue to evolve our programming tools and environments, it’s worth reconsidering the assumptions we make about our craft. There’s still much to learn from early systems like Smalltalk and Self, and their pioneering approaches to human-computer interaction in programming environments.

This project remains open for exploration and contribution, serving as both a practical tool and a reminder that the way we interact with our programming environments significantly impacts how we think about and work with objects in our code.

An HTMLDivElement object's color property is shown being connected to string values 'red' and then changed to 'green' by dragging and dropping the arrow. Each time the property is re-routed the div element changes its color on the screen, demonstrating the live DOM manipulation.


  1. This is true in many languages, even in JavaScript. As a —perhaps unusual— example, we can adapt the previous code to define classes instead of using the object literal notation:

    javascript
    function personNamed(name) {
        const aClass = class {
            static name = name;
            static sayHi() {
                return `Hi, I'm ${this.name}`
            }
        };
    
        return aClass;
    }
    
    const pepe = personNamed("Pepe");
    const marta = personNamed("Marta");

    The resulting objects will understand the sayHi() message producing the same result as before, albeit now being functions and having additional properties. ↩︎

  2. This is the process we earlier inaccurately called “writing classes”. We now understand that was really a particular case of creating static definitions before running the program, rather than working with (dynamic) objects while the program is running. ↩︎

  3. This is from the Ruby Rogues podcast book club, episode 23, minute 53:41; where they talked about the book “Smalltalk Best Practice Patterns”. You can listen to that episode here. ↩︎