A Logo playground for learning to code, where you can watch the turtle think.
Most Logos draw everything instantly. Pendown lets you slow the turtle right down and watch it draw, with the instruction it's carrying out highlighted in your code as it goes. Pause it, step through it an instruction at a time, or speed it up again; Reset puts everything back as it was before Run. While it's paused you can change your program, and Resume carries on with the change. Click a line number to put a breakpoint there, and the program stops each time it gets to that line. You can always see which bit of code made which bit of the picture. Beside the console, two panels show what the highlighted instruction sees: the counter of every loop running, and the value of every variable, in each procedure of the call chain. A value flashes when it changes.
Pendown speaks UCBLogo, the most widely used Logo, with a few modern touches:
- Colours your way: palette numbers,
[r g b]lists from 0 to 255, CSS names like"cornflowerblue, and hex like"#ff8800, with transparency. - Shapes:
CIRCLE,ELLIPSE,SQUAREandRECTANGLE, outlined and filled. - An
ARCthat does what you'd expect: the turtle walks along it. - The whole core language: words, lists and arrays,
FORandWHILE,MAP,FILTERandFOREACHwith templates, andCATCHandTHROW. - Help built in: every command is documented in the app, with examples you can run on the canvas. Press F1 for help on the word under the cursor.
- A tutorial, where Kame the turtle teaches you Logo one step at a time.
- Errors that help: a card over your code says what's wrong, how the command is written, and shows an example that works. A typo gets "Did you mean", and an obsolete UCBLogo command says why it isn't here.
TO PETAL :size
REPEAT 2 [ARC 60 :size RT 120]
END
TO FLOWER :size
REPEAT 12 [PETAL :size RT 30]
END
SETBACKGROUND "midnightblue
SETPENCOLOR "gold
FLOWER 150
SETFILLCOLOR "orange
CIRCLE 20
Pendown is written in Rust, so you'll need a Rust toolchain. On Linux, also install the windowing libraries. On Debian or Ubuntu:
sudo apt install libxkbcommon-dev libxkbcommon-x11-dev libwayland-dev libx11-xcb-devThen build and run it:
git clone <repo-url> pendown
cd pendown
cargo run --release -p pendownRun the tests with cargo test. Debug builds work too, but drawing is much faster in release.
On Linux, to add Pendown to your desktop's app menu, with Kame as its icon:
cargo install --path crates/pendown
install -Dm644 crates/pendown/linux/pendown.desktop ~/.local/share/applications/pendown.desktop
install -Dm644 crates/pendown/linux/pendown.png ~/.local/share/icons/hicolor/128x128/apps/pendown.pngOn X11 the window shows Kame either way. Wayland needs the desktop file to find it.
To build for Windows from Linux, there's a Windows VM in Docker, and a quick check for Windows that doesn't need it: see scripts/windows-vm/README.md. On Windows, Pendown logs warnings, errors and panics to %LOCALAPPDATA%\Pendown\pendown.log, started afresh each run. Elsewhere they go to stderr.
PENDOWN_AUTOTEST=1 cargo run --release -p pendown runs a scripted session in the app: running, pausing, stepping, changing a paused program, breakpoints, Restart and Reset, ending runs, the gallery, the shortcuts, the console, files, errors and the error card, filled shapes, how often the window redraws, the counters and variables panels, and the help panel and tutorial. PENDOWN_AUTOTEST_FROM="Phase 5" starts it at the first section with that in its title. It writes a log and screenshots to target/autotest/, and fails if any of its checks do.
Type a program in the editor and press Run. Use the speed slider to choose how fast the turtle draws. Try the console too: after a run, it knows every procedure your program defined, so you can type FLOWER 80 and watch.
While a program runs, the Repeat counters panel shows every REPEAT, FOREVER, FOR and FOREACH running, outermost first, such as REPEAT 3 of 12, and marks the one REPCOUNT would give. The Variables panel shows the globals, then each procedure in the call chain with its inputs and locals; a variable hidden by a later one with the same name is greyed out. Below them are the pen, fill and background colours, as they were set, each with a square of the colour. At animated speeds, and when paused or stepping, they show exactly what the highlighted instruction sees. At Instant they keep up as best they can.
Pendown remembers how you left it in config.kdl, in ~/.config/pendown (or $XDG_CONFIG_HOME/pendown), or %APPDATA%\pendown on Windows. It's read when Pendown starts, and saved when the window closes, except last-folder, which is saved as soon as you open or save a file:
show-obsolete #false
last-folder "/home/me/drawings"
window width=1400 height=900 state=windowed
panes editor=460 console=190 help=380
speed 5
help open=#true tab=tutorial page=forward search="" pinned=#false lesson=3 scroll=840show-obsolete is only set here. Pendown leaves it alone, and keeps your comments, when it saves. It won't save over a file that isn't valid KDL: it logs a warning and uses the defaults until the file's fixed.
pendown --no-configstarts with the defaults and saves nothing.pendown --config FILEuses another file.- The scripted session uses no file unless it's given
--config.
The Help button, or F1, opens a panel beside the canvas. Help follows the editor's cursor: put it on FD, or on :size, and the panel explains it. Each page has our description, examples with a picture of what they draw, and links to the rest. Run plays an example on the canvas, with its code highlighted in the panel, and Back to my drawing brings yours back. Copy to editor adds it to your program. Browse the contents by chapter, and search by name. To list the UCBLogo commands Pendown leaves out, and why, set show-obsolete #true in config.kdl (see below).
The Tutorial tab has eight lessons with Kame: moving and turning, REPEAT, procedures, helping Kame fix a bug, inputs and variables, recursion, colours and shapes, and exploring L-systems. Each program in them loads into the editor or runs on the canvas.
The Gallery tab shows example programs with a picture of each, in sections: some written for it, such as a snowflake and a city at night, a Halloween set, and the best pictures from the help and the lessons. Load puts one in the editor as a new program, and Run plays it on the canvas. Each card says where it's from, and links to that help page or lesson.
The Shortcuts tab lists every keyboard shortcut: running (Ctrl+Enter or F5), pausing and carrying on (F6), single stepping (F10), Reset (Shift+F5), files, the editor, the console and driving Kame. It shows each key as this computer writes it, ⌘ on a Mac, and asks the keymap for them as it draws, so it's always right. The toolbar's tooltips show their keys too. The keys are one table in crates/pendown/src/shortcuts.rs, which binds them.
Everything the panel shows is in content/: the help is reference.kdl, the lessons are in tutorial/, and the gallery is gallery.kdl, which points at examples where they already live, plus its own programs in examples/gallery/. ucblogo/ has the UCBLogo manual and berkeley-logo.kdl, the manual converted to KDL by scripts/manual_to_kdl.py, which reference.kdl started from; the app doesn't use them.
The help and the lessons are written in a small part of Markdown (crates/pendown/src/markup.rs): paragraphs, `code`, **bold**, *italics*, - lists, # headings, > notes, fenced code blocks, and [FORWARD], a link to a command's help page (or a syntax entry's, by its key in the file, ignoring case, such as [Colours] for colours). Both are built into the app, so rebuild to see a change.
- The help is
content/reference.kdl. Each command has itshelp """…"""text, any number ofexample title="…" """…"""programs (addfails=#trueto one that stops with an error on purpose), andsee "back" "left"for its "See also" links. Leavedescalone: it's the UCBLogo manual's wording. Syntax that isn't a command, such as numbers and quoted words, is in thesyntaxblock at the top. The comment at the top of the file lists every field. - The lessons are the files in
content/tutorial/, one per lesson, starting with its title as a#heading. Every```logoblock is a program with Load into editor and Run. A```pictureblock shows only what its program draws, the shape of the drawing, with the rest of the fence line as its caption (```picture What Kame meant to draw). A```textblock is shown as it is, with no Load or Run. A word after the language names a program, as in```logo tree, socontent/gallery.kdlcan point at it (lesson 6 "tree"). - A new lesson also needs its file added to
SOURCESincrates/pendown/src/tutorial.rs, and the lesson count updated incrates/pendown/tests/help.rs.content/gallery.kdland the scripted session refer to lessons by number, so renumbering lessons means updating them too.
Every help example and lesson program is a test: it must run, and draw the picture saved in crates/pendown/tests/help-pictures/. After adding or changing one, make its picture, then look at the new SVGs before committing:
UPDATE_PICTURES=1 cargo test -p pendown --test examples
cargo test -p pendownpendown-cli runs a program without a window and saves the drawing:
cargo run --release -p pendown-cli -- flower.logo --output flower.png --size 800x600--fit zooms in on the drawing, rather than showing the whole turtle area. An output name ending .svg saves an SVG of the whole turtle area instead of a PNG.
In the app, Export saves the drawing as a PNG, the size of the canvas, or as an SVG of the whole turtle area, which stays sharp at any size.
Pendown measures itself all the time (ADR 0020).
-
Metrics are on in debug builds and off in release.
PENDOWN_METRICS=1turns them on, andPENDOWN_METRICS=0off. -
When a run ends, the app prints a summary on stderr, such as:
run 5.0 s: 58 renders at 12/s (editor 7, console 2), UI 5.28 ms a frame (ours 0.17 ms, canvas 0.18 ms, animator 0.01 ms); ...; CPU UI 6% interpreter 0% other 1%It shows how often the window redrew and what each frame cost, what the interpreter did and how long it waited, and each thread's share of a CPU. The CLI prints its own.
-
F12 shows the same numbers live, over the canvas, in any build.
-
cargo testchecks budgets that don't depend on the machine, such as how many frames a slow run takes. -
perftimes fixed programs and compares them withperf/baseline.json. It fails if anything is more than 20% slower. Run it before merging to main:cargo run --release -p pendown-perf # compare with the baseline cargo run --release -p pendown-perf -- --save # a new baseline, in a commit of its own saying why
A baseline belongs to one machine. On another,
perfsays so and doesn't fail.
| Crate | What it is |
|---|---|
pendown |
The app: editor, canvas, console, help and tutorial |
pendown-cli |
Runs programs from the command line and renders them to images |
pendown-logo |
The Logo interpreter, as a library with no GUI code |
pendown-render |
Draws the turtle's moves into a picture, for the app and the CLI |
pendown-perf |
The perf binary: timings against a baseline |
The crates are in crates/. spikes/ holds the experiments made before phase 1, kept as a record; each builds on its own.
examples/phase-1/andexamples/phase-2/have programs that between them use every command Pendown supports so far.content/reference.kdllists every Logo command, which ones Pendown supports, and how ours differ from UCBLogo.docs/adr/records the design decisions and why we made them.- tree-sitter-logo is a tree-sitter grammar for Logo, and zed-logo is a Zed extension that uses it.
The grammar's queries/highlights.scm lists Pendown's built-in commands, generated from content/reference.kdl, and zed-logo has a copy of the grammar's queries. When tree-sitter-logo and zed-logo are checked out next to this repository, cargo test checks they're up to date, and this updates them after adding commands:
UPDATE_HIGHLIGHTS=1 cargo test -p pendown-logo --test tree_sitterWithout those checkouts, the tests pass without checking. To update by hand:
- Run
cargo run -p pendown-logo --example highlightsto print the list. - In tree-sitter-logo, paste it into
queries/highlights.scm, between; BEGIN GENERATEDand; END GENERATED. - In zed-logo, copy that file to
languages/logo/highlights.scm.
Then commit and push tree-sitter-logo, and zed-logo too. A change to only the queries needs no change to rev in zed-logo's extension.toml, which matters only when the grammar's parser changes.
Pendown is under the GNU General Public License, version 3 or later: see LICENSE.
- Kame's picture,
content/tutorial/kame-turtle.svg, is Turtle from Openclipart, in the public domain (CC0). - The UCBLogo manual,
content/ucblogo/Berkeley-Logo-Reference-Manual.html, is © 1993 the Regents of the University of California, under the GNU General Public License, version 3 or later. Thedescof each command incontent/reference.kdlis taken from it. - The app's font is Noto Sans, by the Noto Project Authors, under the SIL Open Font License: see
crates/pendown/fonts/OFL.txt.