Leporello.js now allows programming 2D graphics and animation using the Canvas 2D API and debugging them with time-travel debugging.
Check out a short demo:
The beautiful tree image in the video is generated by concise and elegant JavaScript code, borrowed from the excellent article Recursive Drawing by Sarah Bricault. The tree is drawn recursively - each branch is drawn by the same function, with only the parameters of size, color, and tilt changing.
The code runs in the Leporello.js environment. At the top, you see the code editor. At the bottom, you see the Call tree view, displaying the function call tree of the program. You can navigate the call tree view by clicking with the mouse or using the keyboard arrows. By selecting a specific function call, you travel back in time to the moment of that call. You can inspect the values of function arguments, local variables, and all intermediate expressions at the chosen call moment. Meanwhile, the 2D canvas displays the image as it looked at the time of the function call.
You can try this example online here. After clicking the link, press the "Re(open) app window" button:

After clicking the button, a new browser tab will open, where the image with the tree will be rendered. You can detach this tab into a separate window and position it on your monitor as you prefer. Then, click on the calltree elements with the mouse or navigate them with the keyboard arrows, and observe how the tree image is drawn as the program executes!
console.logYou may notice that before drawing a branch of the tree, we call the console.log function:
console.log('draw branch', x1, y1, x2, y2)
The output of the console.log function is displayed at the bottom in the Logs panel:

By clicking on the log lines, you move to the corresponding console.log function call in the editor. Simultaneously, the canvas image rolls back to the moment of the console.log function call. Holding and pressing the up or down arrow on the keyboard quickly moves through console.log calls, and the displayed image changes accordingly. You can move both forward and backward:
Now, let's modify the code a bit to make the drawing not instant but with a small delay before each segment. Add the async modifier to the branch drawing function and call it using the await keyword. Additionally, add a sleep function and call it every time after drawing a segment:
function sleep() {
return new Promise(resolve => setTimeout(resolve, 3))
}
//...
await sleep()
Thus, we will get such an animation as in the video below. You can try this example yourself by following the link:
Note that debugging the program using the calltree view remains unchanged compared to the previous example with no animation. Leporello can visualize the calltree for both synchronous and asynchronous code.
To restart the animation, refresh (Ctrl-R) the window where the application is running.
Graphics and animation are excellent ways to teach programming. Leporello.js allows visualizing program execution at a new level. For example, recursive drawing illustrates how recursion works. This makes Leporello.js an excellent environment for teaching programming.
The 2D graphics example demonstrates how a time-travel debugger can be integrated with external stateful systems. Any external stateful system can be connected to the debugger, similar to how canvas is currently connected. You can read more about how Leporello.js changes our views on program debugging in the article Can Debugging Be Liberated from the von Neumann Style?.