Finding Mistakes
When a drawing program does something you did not expect, read its error, print its values, and check how fast it runs. This page covers each of them.
A JavaScript scene cannot be paused at a breakpoint. What follows is what to use instead.
Errors: read them, then fix the line
When your program stops with an error, the preview shows it along its bottom edge, with the line it happened on:
Line 14: speed is not defined [Run again]
Fix the line. About half a second after you stop typing, the scene runs again. Run again on the message, or Run in the footer, starts it without a change. Copy on the message copies the file's name and the error, to paste into a chat; the text can also be selected.
A misspelt function name, such as ctx.filRect, is reported the same way, with the line in your file that made it:
Line 6: ctx.filRect is not a function [Run again]
Three kinds of mistake are quieter:
- A misspelt setting gives no error.
ctx.fillStlye = "red"is allowed in JavaScript: it makes a new, useless setting, and the colour does not change. If a colour, font or line width is ignored, check its spelling. - A mistake in the shape of the code, such as a missing bracket or brace, stops the scene before it starts. The message is shown without a line, as in
Unexpected token '}'. Look at the lines you changed last. You will see messages like this while you are half way through typing a line; they go when the line is finished. - An error inside a listener you added yourself, with
canvas.addEventListener(...), is not shown on the preview. It is in the log.
Printing
console.log() prints to the preview's log. Press LOG in the editor's footer to see it. The first time, Circuitry asks Capture console output?: say yes, and the scene starts again with the log open under the preview. On a phone the log fills the screen, with a close button.
The log has two filters, All and Problems (errors and warnings), a button to clear it, and one to stop capturing. A dot on the LOG button means something new has been logged while the log was closed; a red dot means an error.
let score = 0
function update(dt) {
score += 1
if (score % 60 === 0) console.log("score is", score)
}
function draw() {
ctx.fillStyle = "black"
ctx.fillRect(0, 0, width, height)
}
Print now and then, as above, rather than every frame: sixty lines a second is hard to read.
To see a value without opening the log, draw it: ctx.fillText(String(score), 12, 24) at the end of draw().
The frame rate
Frame rate in the footer shows the frames drawn each second in a corner of the preview, as in 120 fps. The number is held steady, so it does not flicker between two values. The choice applies to every file.
To time one part of a frame, wrap it in measure, and it gets a line of its own under the frame rate, as in physics 0.90 ms:
function update(dt) {
measure("physics", () => {
for (const ball of balls) ball.update(dt)
})
}
measure costs nothing while the frame rate is not showing.
To show the frame rate as part of your own picture, work it out. update(dt) is given the seconds since the last frame, so 1 / dt is the frame rate:
let fps = 60
function update(dt) {
fps += (1 / dt - fps) * 0.05 // smoothed, so the number can be read
}
function draw() {
ctx.fillStyle = "#101820"
ctx.fillRect(0, 0, width, height)
ctx.fillStyle = "lime"
ctx.font = "16px monospace"
ctx.fillText(`${Math.round(fps)} fps`, 12, 24)
}
Most screens show 60 frames a second, and some 120.
If the number drops, your program is doing more each frame than the device can keep up with. The usual causes:
- Very many separate drawing calls. Thousands of shapes a frame is fine; tens of thousands is slow. Draw fewer, bigger things, or join many lines into one path and
stroke()it once. - Heavy work every frame. Work things out once at the start and keep the results.
- Reading pixels every frame.
ctx.getImageDatacopies every pixel it is asked for. Other reads cost nothing (see Tips and limits). - Printing every frame. Sixty lines a second fill the log and slow the scene.
When the preview is frozen or blank
If the preview stays blank and Circuitry stops answering clicks, a loop is stuck. The usual cause is a while (true) loop that never waits: every loop that runs forever must await frame() (or await sleep(...)) inside it. After about four seconds the loop is stopped, and the preview shows an error that names the loop's line. See Tips and limits.
If the preview is blank but everything else still works, look for an error along the bottom of the preview, and check that the scene paints something: a 3D scene shows nothing until renderer.render(scene, camera) is called in draw().
Related
- How a program is shaped
- Tips and limits
- Debugging in Pyctures, where a Python scene can stop at a breakpoint