Codetoy draws in immediate mode: nothing on screen is an object you keep around. Each frame you describe the whole picture, top to bottom, back to front. The API mirrors the HTML canvas 2D context, simplified for games.
#Coordinates
The canvas is measured in pixels with the origin at the top-left corner. X grows to the right and Y grows downward.
(0, 0) ──────────────► x
│
│ • (centerX, centerY)
│
▼ y (width, height)The canvas fills the preview and can change size. Read screen.width()Screen.Widthwidth(), screen.height()Screen.Heightheight(), screen.centerX()Screen.CenterXcenterX() and screen.centerY()Screen.CenterYcenterY() every frame instead of hard-coding sizes, so your program works on phones, embeds and Discord.
#Clearing the screen
The canvas keeps whatever was drawn last frame. Start every frame by painting a full-screen rectangle, or call reset()Canvas.Reset()reset(), which clears the canvas and resets colors, line styles and transforms.
Trails for free
Paint the background with some transparency, for example fill()Canvas.Fill()fill() with alpha 0.15, and moving shapes leave fading trails.
#Colors
Drawing uses two colors: fill for the inside of shapes and text, and stroke for outlines and lines. Both stay set until you change them.
fill()Canvas.Fill()fill()andstroke()Canvas.Stroke()stroke()take red, green and blue from0to255, plus an optional alpha from0.0to1.0.fillStyle()Canvas.FillStyle()fillStyle()andstrokeStyle()Canvas.StrokeStyle()strokeStyle()accept any CSS color string:"#ff6600","tomato","hsl(200 80% 60%)".noFill()Canvas.NoFill()noFill()andnoStroke()Canvas.NoStroke()noStroke()turn either one off.
Fill and stroke are both on when a program starts, and the default stroke is a thin black outline. Call noStroke()Canvas.NoStroke()noStroke() once at the top of export function update()Screen.OnUpdate()def update() for flat shapes. Setting a color with fill()Canvas.Fill()fill() or stroke()Canvas.Stroke()stroke() turns that one back on.
#Shapes
| Shape | Function | Notes |
|---|---|---|
| Rectangle | rect()Canvas.Rect()rect() |
x, y is the top-left corner. An optional fifth argument rounds the corners. |
| Circle | circle()Canvas.Circle()circle() |
x, y is the center. |
| Ellipse | ellipse()Canvas.Ellipse()ellipse() |
x, y is the center. w, h are the horizontal and vertical radii. |
| Line | line()Canvas.Line()line() |
Drawn with the stroke color and line width. |
| Triangle | triangle()Canvas.Triangle()triangle() |
Three corner points. |
| Polygon | polygon()Canvas.Polygon()polygon() |
A flat list of points: [x1, y1, x2, y2, …]. |
| Arc | arc()not in C#arc() |
Part of a circle, from a start angle to an end angle, in radians. |
Every signature is in the Canvas API.
#Text
Set a font with font()Canvas.Font()font(), then draw with text()Canvas.Text()text(). Text uses the fill color, and x, y is the top-left corner of the text. measureText()Canvas.MeasureText()measureText() returns a string's width in the current font, which is how you center text:
#Transforms
translate()Canvas.Translate()translate(), rotate()Canvas.Rotate()rotate() and scale()Canvas.Scale()scale() move the coordinate system for everything drawn after them. Wrap them in push()Canvas.Push()push() and pop()Canvas.Pop()pop() so they only affect what's in between. push()Canvas.Push()push() saves the colors, line settings and transform, and pop()Canvas.Pop()pop() restores them. It doesn't save whether noFill()Canvas.NoFill()noFill() or noStroke()Canvas.NoStroke()noStroke() is on.
Transforms combine in order. Translate first, then rotate, to spin something around its own center. resetTransform()Canvas.ResetTransform()resetTransform() drops all transforms without touching colors.
#Custom paths
For curves and arbitrary shapes, build a path and then fill or stroke it:
#Performance tips
- A few hundred to a thousand shapes per frame is comfortable. Watch
screen.fps()Screen.FPSfps()if things slow down. - Avoid creating new arrays or lists every frame. Reuse them.
- Static backgrounds still need repainting every frame, so keep them simple.