Spacepaint is turtle graphics reimagined for a 2026 introductory programming course. Instead of steering a turtle, students fly a small spacecraft through an origin-centered 3D scene and paint with sigma aura beams.
Install uv, then use Python 3.11:
uv sync --managed-python
uv run spacepaintSpacepaint starts by running paint(ship) from student_code.py. That module
records a program; the window then plays it from the beginning. Restart the app
after editing student code.
From the initial camera:
+Y (up)
|
|
-X ----------+---------- +X (right)
/
+Z (toward you)
The ship starts at (0, 0, 0), facing +X. Distances are world units and
angles are degrees. The default ambient universe fills -100 through +100 on
all three axes.
from spacepaint import Ship
def paint(ship: Ship) -> None:
ship.beam_color("cyan")
ship.beam_on()
ship.forward(6)
ship.turn_left(90)
ship.forward(3)
ship.beam_off()
print(ship.state.position)Movement methods:
forward(distance)andbackward(distance)follow the ship's heading.yaw_left(degrees)andyaw_right(degrees)point the nose left or right around the ship's own up axis.turn_leftandturn_rightare beginner-friendly aliases with the same ship-relative behavior.pitch_up(degrees)andpitch_down(degrees)raise or lower the nose around the ship's own left-to-right axis.roll_left(degrees)androll_right(degrees)lower one wing and raise the other around the ship's own nose-to-tail axis.arc(radius, degrees)follows a circular arc in the ship's current plane. Positive radii bend left and negative radii bend right, matching turtle graphics conventions.
Yaw, pitch, and roll rotate the craft in place around its center. Their axes move with the ship, matching standard aircraft controls.
Beam and playback methods:
beam_on()paints permanently;beam_on(fade_after=5)makes emitted beam samples remain for five seconds and then fade.beam_off(),beam_color(name_or_hex),beam_width(width), andclear_beams()control the artwork.fill_on(opacity=72/255)enables planar fills at an opacity from0.0to1.0; zero disables fill and1.0is fully opaque.fill_off()disables fills, andfill_mode(enabled, opacity=72/255)is the boolean form. A fill takes the current beam color and opacity when it starts, including the default cyan or last selected color when the beam is off. Closing an outline or painting into a new plane makes the next filled motion start a new surface.set_speed(multiplier)changes the speed of later movement and turns.set_speed(0)acts as a breakpoint and pauses before the following command. Left/right arrow keys can play next/previous commands.
Named colors are white, red, orange, yellow, green, cyan, blue,
purple, magenta, and pink. Hex colors use #RRGGBB.
Window controls: left-drag orbits around the current camera target, the scroll
wheel zooms, Space pauses, and Left/Right step backward or forward by one queued
command while paused. R replays, A toggles labelled X/Y/Z axes, and V
toggles between the default artwork view and a smoothly followed ship view.
Artwork view tracks the center of the ship-and-beams bounding box and
automatically zooms out with extra framing room to keep the whole drawing
visible. 0 resets the current camera view, and H hides the HUD. The HUD also
has view and axes toggle buttons.
Axis minor ticks mark each unit; numbered major ticks mark every five units. Opposite arrow presses cancel the most recently queued step.
Spacepaint always renders a bright beam core plus additive halo geometry. When
bloom is enabled, macOS uses a four-pass Panda3D FilterManager pipeline with
GLSL 120 compatibility shaders; other platforms use Panda3D's CommonFilters
bloom. If post-processing setup fails, Spacepaint keeps the layered geometry
and continues without bloom.
The render configuration exposes enable_bloom, bloom_threshold, and
bloom_intensity for testing and tuning. Geometry is antialiased with 4x MSAA
by default, including the offscreen macOS bloom scene, and its final composite
also uses FXAA to smooth any remaining high-contrast edges. Set
antialias_samples to 0, 2, 4, or 8, and toggle enable_fxaa when
testing. The HUD is composited separately so text remains crisp. No OpenGL/GLSL
version PRC override is required or supported by this project.
uv run pytest
uv run pyrightRendering changes also require screenshots under artifacts/, as described in
URSINA_PROJECT_SETUP.md and AGENTS.md.
Copyright (c) 2026 Kris Jordan kris@cs.unc.edu. Spacepaint is licensed under the MIT License.
Development of this codebase was assisted by Codex using the 5.6 Sol model.
Assisted-by: Codex:5.6-Sol