Core Concepts /Language Notes

Core Concepts

Language Notes

The things worth knowing about your language in Codetoy, from types and imports to common gotchas. This page changes with the selected language.

#Imports

Codetoy's API comes in four modules. Import only what you use, either by name or as a namespace. These docs use names for canvas and time, and namespaces for screen and input:

AssemblyScript
import { fill, rect, circle, text, font } from "codetoy/canvas"
import * as screen from "codetoy/screen"   // screen.width(), screen.centerX() ...
import * as input from "codetoy/input"     // input.mouseX(), input.isKeyDown() ...
import { deltaTime, elapsedTime } from "codetoy/time"

console and Math are global and need no import.

#Number types

AssemblyScript compiles to WebAssembly, so numbers have concrete types:

Type Use for
f64 Positions, speeds, angles: almost everything in the API
i32 Counters, indices, mouse buttons, touch ids
bool Flags

Integer literals default to i32. Give variables an explicit type when you mean a decimal:

AssemblyScript
let x = 0          // i32! x += 0.5 is a type error
let y: f64 = 0     // what you usually want

Convert between types with as: (i as f64) / 5.0, or <i32>Math.floor(x).

#Strings

Join with + and convert numbers with .toString(). Template literals work too:

AssemblyScript
console.log("Score: " + score.toString())
text(`FPS: ${Math.round(screen.fps())}`, 10, 10)

#Arrays and classes

Arrays are typed: new Array<f64>() or const pts: f64[] = [0, 0, 10, 10]. Classes work like TypeScript classes, including constructor parameter properties.

#Operator overloading

AssemblyScript lets classes define operators, which makes vector math read naturally. The editor's TypeScript checker may underline it, but it compiles and runs:

AssemblyScript
class Vec2 {
  constructor(public x: f64, public y: f64) {}

  @operator("+") add(o: Vec2): Vec2 { return new Vec2(this.x + o.x, this.y + o.y) }
  @operator("*") scale(s: f64): Vec2 { return new Vec2(this.x * s, this.y * s) }
}

let pos = new Vec2(100, 100)
const vel = new Vec2(120, 40)

export function update(): void {
  pos = pos + vel * deltaTime()
}

#Differences from TypeScript

  • No any, no undefined, and no union types except nullable classes (Foo | null).
  • Closures can't capture local variables. Use top-level variables or class fields.
  • Annotate the return type of functions you export, e.g. export function update(): void.

More detail: AssemblyScript concepts and feature status.

#The API is global

Canvas, Screen, Input and Console are static classes available everywhere, with no using needed. Common .NET namespaces like System and System.Collections.Generic are imported implicitly, so Math, List<T> and Dictionary<K, V> just work.

#Properties vs. methods

Values that are always current are fields: Screen.Width, Screen.CenterX, Screen.DeltaTime, Screen.FPS, Input.MouseX. Queries that take an argument are methods: Input.IsKeyDown("a"), Input.TouchX(id).

#Top-level statements

Codetoy supports C# 14, so you can write code directly at the top of the file. A classic static void Main works too.

Declare variables before types

With top-level statements, all variables and statements must come before any class, struct, record or enum declarations. Put your types at the bottom of the file.

C#
// ✓ statements first
double x = 0;
var player = new Player();

Screen.OnUpdate(() => {
    player.Draw();
});

// ✓ types last
class Player {
    public double X = 100, Y = 100;
    public void Draw() => Canvas.Circle(X, Y, 20);
}

#Callbacks replace each other

Screen.OnUpdate and every Input.On… method store one callback. Calling it again replaces the previous one, so put all your per-frame logic in a single OnUpdate.

#Logging

Use Console.Log, Console.Warn and Console.Error. Codetoy's Console class shadows System.Console. String interpolation is the easiest way to format: Console.Log($"x = {x:F1}").

#Not available in C# yet

Canvas.Arc, elapsed time, frame count and a resize callback. Track elapsed time yourself by adding up Screen.DeltaTime. Builds and Discord Activities also need AssemblyScript for now.

#Imports

Codetoy's API comes in four modules:

Python
from codetoy.canvas import fill, rect, circle, text, font
from codetoy.screen import width, height, centerX, centerY, fps, frameCount
from codetoy.time import deltaTime, elapsedTime
from codetoy._input import mouseX, mouseY, isKeyDown, isMouseDown

import codetoy.canvas as canvas works too if you prefer canvas.fill(...). Note the input module is codetoy._input, with a leading underscore.

#Python runs on Pyodide

Your code runs in real CPython compiled to WebAssembly (Pyodide). The standard library is available, including math, random, dataclasses, collections and itertools. The first run takes a few seconds while Python loads.

#Event functions are found by name

Define update, resize, keyDown, keyUp, mouseDown, mouseUp, touchStart, touchMove and touchEnd at the top level of your file. Codetoy looks them up after your script runs. Note that the names have no on prefix.

#global is required to assign

Reading a module-level variable inside a function works, but assigning to one creates a new local unless you declare it global:

Python
score = 0

def update():
    global score
    score += 1

Mutating a list or dict doesn't need global: enemies.append(e) is fine.

#Lists for polygons

Pass a plain Python list of numbers to polygon: polygon([x1, y1, x2, y2, x3, y3]).

#Logging

print() writes to the console. Errors and anything printed to sys.stderr show as errors. There's no separate warning level.

#Performance

Python is slower than the compiled languages. Keep the work in update() modest: hundreds of shapes, not tens of thousands. Move heavy setup to module level so it runs once.

#Not available in Python yet

Builds and Discord Activities need AssemblyScript for now.

Esc