WoofJS — making JavaScript learnable

Steve Krouse
12 min readJul 12, 2016

--

“I’m done with Scratch,” my students tell me. “Now what?” I’ve been trying to answer this question for the past two years: what’s the programming language to learn after Scratch?

Scratch is amazing. With it, any eight-year-old can learn how to make video games in just a few hours. With any other programming language, teaching sequential evaluation, parameters, events, parallel programming, conditionals, loops, Boolean algebra, variables, and lists would take dozens of agonizing hours. In Scratch a student joyously becomes familiar with these concepts through making their first game. As a co-founder of The Coding Space, a NYC after-school and summer program, I have watched students spend thousands of hours in Scratch. It’s magical to see.

However, all good things must come to an end. Despite having an incredibly low barrier to entry, Scratch has a ceiling that limits what types of games students can build. Multiplayer is impossible, and so are full screen and mobile games.

Requirements

There are hundreds of programming languages to sort through, so I started with two non-negotiable requirements this next language must have:

  1. Motivating. As video games, animations or stories, Scratch projects are worth sharing. Students never ask, “why are we learning variables?” because they are self-motivated to learn variables to make the score counter work in their racing game.
  2. Shareable. Every Scratch project is immediately shareable via a URL. There’s drastically less motivation to make something that you can’t easily show your friends or family.

These constraints knock out almost every programming language right off the bat. Ruby, Python, Java, and C++ projects can’t easily be shared via a URL, nor can you easily build motivating projects with them (unless you find the Fibonacci sequence or REST APIs motivating).

Web Programming

Barring all of the usual suspects, the answer seems obvious: let’s teach kids to make websites! I tried, and it’s not that easy. JavaScript is the programming language of the web. It controls everything that happens on a website, like pop-ups, animations, warnings, and autocomplete search boxes. However in order to make a website, a student also needs to know HTML and CSS. These control how a website looks, including all text, pictures, fonts, colors, shapes, sizes and layouts.

This is a big jump from Scratch. Not only do students have to learn how to translate their Scratch coding knowledge to JavaScript, but they also have to learn two additional languages, HTML and CSS. Considering this is an 11-year-old’s first experience with text-based coding, three new programming languages all at once is a nightmare. So let’s simplify.

ProcessingJS

Processing is a graphical programming framework that is widely used to teach beginner programming. Khan Academy uses ProcessingJS, the JavaScript version of Processing, for its introductory coding courses. The magic of ProcessingJS is that it allows students to build interesting projects, like games and animations, solely with JavaScript — no HTML or CSS required.

Despite many novel features, ProcessingJS and Khan Academy’s programming environment are far from ideal. Bret Victor makes a comprehensive study of these flaws in his essay “Learnable Programming.”

I searched for a JavaScript graphics framework that didn’t have the drawbacks of Processing. I couldn’t find one, so I built it.

WoofJS

It is my pleasure to announce WoofJS v1.0.0. At long-last, Woof is my answer to the question, “What comes after Scratch?”

It meets our requirements:

  1. Motivation. Like Scratch, Woof’s lends itself incredibly well to games and animations, which are highly motivating.
  2. Shareable. Built for the web, Woof works seamlessly in all browsers, mobile and desktop, full-screen and fixed-screen. You can even bookmark your game on your home-screen like an app.

And it does one better: Woof closely mimics Scratch.

Because Woof commands directly correspond to Scratch blocks, they allow students to focus solely on syntax translation. Students don’t have to learn new metaphors and paradigms at the same time they’re picking up their first text-based syntax. Going from move 10 steps in Scratch to car.move(10); in Woof is a small enough jump that students familiar with Scratch can pick it up in just a few hours.

WoofJS & ProcessingJS

Reading Bret Victor’s “Learnable Programming” during my freshman year of college started me down the path of teaching computer science, so bear with me as I pay homage to an idol. In his essay, Bret proposes a checklist to evaluate programming languages for learnability. Using these questions, let’s see how WoofJS takes some of the best parts of ProcessingJS and builds upon them by borrowing the best parts of Scratch:

Is meaning transparent? Is meaning explained in context, by showing and telling?

In Processing, you set properties via positional parameters. This makes it tough for students to understand what different parameters do.

In Woof, all object properties can be set explicitly as named-parameters in the constructor or through dot-notation. It’s transparent what new Rectangle() does, as well as what each of its properties do.

// WoofJS// set optional parameters explicitly by name
var r1 = new Rectangle({x: 100, y: 200, width: 30, height: 40});
// set properties by name
r1.color = "blue";

Is time visible and tangible?

In Processing, if you want something to animate, you have to add effectual code in the draw() method. Having to think about how things are rendered on the screen bogs programmers down with low-level details. This pattern is un-intuitive and error-prone even for advanced programmers.

// ProcessingJSvar x = 0;
var y = 0;
draw = function() {
background(255, 255, 255);
ellipse(x, y, 30, 30);
x++;
y++;
};

In Woof, (like ReactJS) rendering is taken care of for you. You don’t have to add effectful code to your draw() method to make things move. Instead, you cause state to change in response to events or intervals. This means you don’t even have to think about how your view layer works — it just does. And in Woof, time is much more tangible with commands like forever(), after(), when(), and repeatUntil().

// WoofJSvar circle = new Circle();forever(function() {
circle.x++;
circle.y++;
});

Does the environment show the data? Is hidden state eliminated?

In Processing, when you want to rotate or change something’s color, you have to change the rotation and color for every shape that comes after it. You have to be careful to keep track of this hidden state or your entire project will be the same color and tilted.

// ProcessingJSfill(252, 60, 115);         // set the color pink
rect(100, 160, 20, 80); // create a pink rectangle
fill(84, 98, 255); // set the color blue
ellipse(280, 200, 80, 80); // create a blue circle

In Woof, there’s no hidden view-layer state. If you make one rectangle pink, only that one rectangle is pink. As an added bonus, Woof makes it easy to examine all internal state in real-time.

// WoofJSnew Rectangle({color: "pink"});
new Circle({color: "blue"});
var counter = 0;
forever(function() { counter++; });
new Text({text: function() { return counter; } })

Is something on screen as soon as possible?

In ProcessingJS, you have to correctly type every parameter in order to get something to even show up on the screen.

// ProcessingJSellipse();                   // nothing on the screen
ellipse(200); // nothing on the screen
ellipse(200, 200); // nothing on the screen
ellipse(200, 200, 30); // nothing on the screen
ellipse(200, 200, 30, 30); // circle appears on the screen

In Woof, simply start off by typing new Circle() and you’ll see a circle on the screen. If you want to make it bigger, you can add a radius. Want to move it to the right? No problem!

// WoofJSnew Circle();                      // circle appears on the screen
new Circle({radius: 30}); // circle appears with radius 30
new Circle({radius: 30, x: 200}); // circle appears at x 200

Can the programmer start concrete, then generalize?

As Bret lists in his essay, creating by abstraction happens in two ways:

  1. Start constant, then vary

As we demonstrated in the Is time visible and tangible? section above, Processing has a large cognitive gap between a stationary circle and a moving circle, while Woof’s cognitive gap is almost as small as Scratch’s.

2. Start with one, then make many

As we’ll show in the Can the programmer put diverse pieces together? section below, Processing makes it difficult to group code into reusable chunks, which makes it tricky to start with one and then make many without unforeseen side-effects.

Because everything in Woof is an object (or sprite), it’s easy to start with one object and then expand to as many objects as you want. Each object is responsible for its own data and can travel. You can even store them in a list. (In Processing, the closest thing to keeping track of many things of a similar type would be a list of functions.)

// WoofJSnew Circle({x: randomX(), color: randomColor()}) // start with onevar circles = []                                 // then make many
every(1, "second", function() {
circles.push(new Circle({x: randomX(), color: randomColor()}));
});

Is the computer’s world connected to the programmer’s world?

Bret Victor says it best:

Processing’s core metaphor is the ‘painter’s algorithm’ — the computer places a series of shapes on the screen, like drawing on paper. Because this metaphor carries no computational power (you cannot compute by filling in pixels), all computation occurs outside the bounds of the metaphor…

// ProcessingJSvar x = 0, y = 50, dy = 0;
draw = function() {
x+=4
y+= dy;
if (y > 185) {
dy = -dy
ellipse(x, 190, 36, 25)
}
else {
dy = dy * 0.98 + 3;
ellipse(x, y, 30, 30)
}
};

The simulated properties of the ball (position) are not associated with the picture of the ball onscreen. They are computed and stored abstractly as “numbers” in “variables”, and the ball is merely a shadow that is cast off by this ethereal internal representation. The ball cannot be picked up and moved; it cannot be told how to interact with other objects. It is not a “living thing”, and the simulation cannot be understood or thought about in any way other than “numbers in variables”. This is a very weak way of thinking.

Woof steals Scratch’s and Smalltalk’s everything-is-an-object (or sprite) metaphor. Woof also steals LOGO’s movement, angles, turning and pen anthropomorphic metaphors. This way, the x- and y- position of your objects (or sprites) are stored within the objects themselves. If you tell them to .move() or .turnLeft() they’ll listen to you like a dog would. This makes it much easier to mentally think through an algorithm, because you can pretend your code is giving someone orders or that you yourself are in the object’s shoes and are carrying out orders yourself.

// WoofJSvar dog = new Image({url: "http://i.imgur.com/SMJjVCL.png"});dog.penDown = true;dog.turnLeft(90)dog.move(10)

Can the programmer break down her thoughts into mind-sized pieces?

In Processing, all code has to funnel through a global render() loop and through global events, like onMouseDown(). This makes it easy to write monolithic spaghetti code and much more difficult to write decomposable code.

// ProccesingJSdraw = function() { doStuff1(); };         // ignoreddraw = function() { doStuff2(); };         // overwrites doStuff1()onMouseDown = function() { doStuff3(); };  // ignoredonMouseDown = function() { doStuff4(); };  // overwrites doStuff3()

In Woof, you can have infinitely many forever() and other control loops. You don’t have to funnel all of your code through the same top-level events that forces you to build tangled code. As an added benefit, sprites (or objects) have their own events, and can listen to as many of them as you want.

// WoofJS forever(function() { doStuff1() })           // stuff1 happens!forever(function() { doStuff2() })           // stuff2 happens!car.onMouseDown(function() { doStuff3() })   // stuff3 happens!car.onMouseDown(function() { doStuff4() })   // stuff4 happens!

Can the programmer put diverse pieces together?

Brett Victor again:

Processing’s lack of modularity is a major barrier to recomposition. The programmer cannot simply grab a friend’s bouncing ball and place it alongside her own bouncing ball — variables must be renamed or manually encapsulated; the “draw” and mouse functions must be woven together, and so on. One can easily start from an existing Processing program and modify it, but the language does not encourage combining two programs.

In Woof, because all view-layer state is encapsulated into objects, it allows you to easily build modular and functional code without worrying about side effects. No need to interweave top-level events — your events and your friend's events can coexist happily side-by-side.

// WoofJS// take a friend’s stem code
new Rectangle({y: minY, height: height, color: "green"});
new Circle({y: minY / 2 - 20, x: -20, radius: 20, color: "green"});
new Circle({y: minY / 2 +40, x: 20, radius: 20, color: "green"});
// and add your petal code, no problem
for (var i = 0; i < 10; i++){
new Rectangle({width: 100, height: 100, color: "red", angle: 36 * i});
}

Early Reception

We at The Coding Space see over 100 students a week that have collectively built more than 1,500 projects in the last year. Most of them work in Scratch, but a growing number are graduating to JavaScript. Before Woof, this was a incredibly frustrating experience for students, and painful to see as a teacher. In Woof, things aren’t quite as magical as in Scratch, but they are a whole lot better than without it.

It took one of our 13-year-old students just a few minutes pick up Woof and make a circle that grows with your mouse:

var circle = new Circle();
forever(() => {
circle.radius = circle.distanceTo(mouseX, mouseY);
});

One of our 11-year-old GirlCode summer students was able to pickup JavaScript and Woof within a week, producing numerous games including a Flappy Bird clone (space key to play). Before Woof, even my advanced 13-year-olds could barely build a clone of sometimesredsometimesblue.com after weeks of struggling, so this is progress.

A few teachers at The Coding Space have contributed to various games and demos:

v1.0.0 and beyond

For me, version 1 signifies that while WoofJS is new software, its API is reasonably good and won’t be changed drastically in the coming months. This means it’s now safe to build curriculum and tools on top of Woof. However, new backwards-compatible features and bug fixes will be added frequently, so keep the issues coming!

If you’re a coding teacher, you can get your students started by having them re-make one of their existing Scratch projects in Woof. Because they’re starting with algorithms they already know, your students will be able to focus solely on the tricky problem of syntax translation from Scratch to JavaScript. From there, it’ll be easier for them to create new projects directly in Woof.

Down the line, I think it would be interesting to integrate Woof with either Pencil Code’s Droplet editor or Google’s Blockly to make the transition from Scratch to Woof even easier. Ping me at steve at thecodingspace.com if that sounds like a project you’d like to help with!

In the meanwhile, I encourage you to check out our documentation, play with some demos, and build your own games and animations. Even better, go find a 12-year-old that’s ready to move on from Scratch and show her what’s next.

Acknowledgements

Thank you Sally Matson for your highly-encouraging enthusiasm in the first few days of Woof and for building the first addicting Woof game. Thank you Nicole Kelner and Eli Kariv for helping to make The Coding Space a place where projects like Woof can be brought to life, and thank you Nicole for your line-edits. Thank you Enzo Acquaviva for your curricular help, API advice, and for totally reorganizing the documentation around Scratch blocks. Thank you Sophie Gorham for bringing mouse-speed to Woof with your hilarious demo. Thank you to our very first student Ben for help with Woof functions and demos. Thank you Paul Kogan for believing so strongly in Woof that you sustained my own belief in it. Thank you Hudson for showing me that Woof was technically feasible through our hacking sessions, and that it was worth making for students like you.

Shameless plug for The Coding Space: We’re hiring full-time and part-time teachers so please reach out if you’re in NYC, love working with children, and are excited to shape the future of coding education.

--

--

Steve Krouse

Enabling computational thinking by building tools for thought at futureofcoding.org. Co-creator of thecodingspace.com and woofjs.com