Read first: one-page quick-start guide for teachers
Python Tutor is a free tool that has been used by tens of millions of people since 2010 to visualize and debug code step-by-step. Despite its name, it visualizes more than just Python: I added JavaScript support back in 2015, and it also does Java and C/C++. This article shows instructors what the JavaScript visualizer can illustrate, from a student's first loop up through closures.
This tool teaches the JavaScript language, not web development. JavaScript is the language that runs in every web browser, and through Node.js it runs on more servers and command-line tools too, which probably makes it the most widely-run programming language in the world. Everything your students will eventually do with HTML, CSS, and the DOM depends on knowing the core language, which is what this tool helps with.
These visualizations also fit into high school AP Computer Science Principles (CSP) curriculum. AP CSP doesn't require any particular programming language (the exam presents code in pseudocode), but JavaScript is one of the most popular choices. Code.org's CSP and CodeHS's AP CSP course both teach using JavaScript.
If you think this tool may be helpful for your colleagues or students, share this direct link in relevant course materials, chat groups, mailing lists, discussion forums, or social media:
When students learn their first loops, teachers often have them trace code by hand, keeping a table of each variable's value at each step. Python Tutor does this automatically:
At the current step (Step 12), we're partway through the loop: day is
2 and total is 50.
The visualizer shows each value's type: strings display in quotes,
numbers don't, and special values like null and undefined display
by name. What value does each variable hold when this program ends?
Note how b is the string "12" since + concatenates when either
side is a string, but c is the number 9 since - converts both
sides to numbers. nothing is null while missing is undefined.
And same is false: click "Next >" to run the last line and see
that sum prints as 0.30000000000000004, not 0.3.
Each function call gets its own frame that holds its parameters and local variables:
Here at Step 7, the second call to priceWithTax is running, and the
first call's result (54) already sits in the global variable jeans.
Step forward to see this call return 27 and its frame disappear.
Here's a classic array aliasing example that trips up beginners:
b = a doesn't copy anything. Both variables now refer to the
same array object, and the diagram shows it clearly. In contrast
a.slice() creates a real copy, which appears as a second array in the
visualization.
The same thing happens when arrays get passed to functions:
In the current step, replaceAll has just reassigned its parameter
arr to a brand-new ["coffee"] array, and there are now two distinct
arrays.
Here's an array of two objects:
Map and Set data structures are rendered in a similar way:
The visualizer draws classes, instances, and this inside of method calls:
Here at Step 10, alice.deposit(25) is executing, and the deposit
frame's this points at Alice's object.
Here's an example of inheritance and polymorphism:
At Step 18 the loop is calling pets[i].speak() on Rex, so this
points at the Dog instance and calls Dog's version of speak().
Step back to Step 12 to see that same call go to Animal's method.
Recursion is shown by all active frames on the stack, each with its own copy of the local variables. Here's the classic ultra-cliched factorial:
Functions are values in JavaScript, so they can be passed to other
functions. Here applyToEach takes an array and a function f, and
applies f to every element:
At Step 14, the global double and the parameter f both point at the
same arrow function object (x => x * 2), which is being called with
x = 20.
The same kinds of frames appear for built-in array methods like map
and filter:
At this step, map has already built a new [ 20, 40, 60 ] array, and
filter is calling its arrow function with x = 20.
The visualizer also supports closures:
Right now makeCounter has already returned, but code can still access
its local count variable. The global tickets points at the inner
increment function, and each call to tickets() gets a frame with a
slot labeled parent:count (this isn't an ideal way to display it, but
the older JavaScript debugger we used didn't give more detailed info).
This example shows unexpected behavior if you use var instead of let
or const to declare variables:
At the current step (Step 2), the code printed undefined rather than
crashing since JavaScript hoists var declarations up to the top of
their scope before running it. Click "Edit Code" and change var to
let to get more predictable behavior.
document, window, the DOM, alert(),
prompt(), confirm(), and frontend frameworks like React. This tool
is only for learning how the core language works.setTimeout,
setInterval, promises, async/await, and event handlers don't
work. There's also no way to read keyboard input (though this is
work-in-progress).async/await, object spread {...obj}, optional
chaining ?.) will not run. (We are working on updating this now,
though.)The JavaScript visualizer in Python Tutor can help your students see what their code actually does. It's free, it runs in the browser with nothing to install, and it has been used by tens of millions of people.
Feel free to share this direct link in relevant course materials, chat groups, mailing lists, discussion forums, social media, or anywhere else:
And if you also teach Python, Java, or C/C++, check out my companion articles on what the Python visualizer, Java visualizer, and C/C++ visualizer can do.