Documentation · Reference 2D Canvas API
v0.1 · beta

DanaProcessing

DanaProcessing's own audio engine, written in C#: live loops that stay in time while you edit, synths, drums and samples — and visuals that react to the exact moment each note is heard. No external synth, no extra package.

A Processing-style creative coding framework for C#. Write a Sketch, override Setup() and Draw(), and get the same drawing vocabulary Processing sketchers already know — in 2D on SkiaSharp, or in real 3D on OpenGL, both through the desktop IDE built just for this.

01 · Overview

What DanaProcessing is

DanaProcessing ports the shape and feel of the Processing language to C#. Instead of Java's color() function and bit-packed ints, colors are a real Color struct. Instead of a fixed window, a Sketch can be hosted inside Avalonia or the bundled IDE. Everything else — the mental model of Setup() once and Draw() every frame, fill/stroke state, beginShape()/endShape(), noise() — is carried over on purpose.

The same Sketch has a second renderer built in: pass RendererKind.Renderer3D to Size() and the exact same Setup()/Draw() model gets you real OpenGL geometry — Box()/Sphere(), lights, cameras, materials, custom shaders — no separate engine, no second API to learn (see Getting started in 3D). All of it runs inside a desktop IDE built specifically for this workflow: diagnostics underline mistakes as you type, a // nuget: comment resolves a package before you hit Run, and Run itself compiles in memory and swaps the canvas in well under a second (see The IDE).

Where things live. Drawing state and operations are defined once on GraphicsContext, inherited by both Sketch and PGraphics. Sketch adds lifecycle, input, time, and randomness — things an offscreen buffer doesn't need.

Minimal sketch

The smallest complete sketch: Setup() sets the canvas size once, Draw() runs every frame and paints a circle that follows the mouse.

public class MySketch : Sketch
{
    public override void Setup()
    {
        Size(600, 400);
    }

    public override void Draw()
    {
        Background(20, 20, 30);
        NoStroke();
        Fill(100, 200, 255);
        Ellipse(MouseX, MouseY, 60, 60);
    }
}

Save this in the IDE and press Run — SketchCompiler compiles it in memory with Roslyn and swaps it straight into the canvas.

02 · Structure

Sketch structure

Every sketch inherits from Sketch and implements Draw(). A host calls Setup() once, then Draw() on every frame, at the rate set by FrameRate().

Setup() → void

Runs once before the first frame. Use it to set the canvas size and any one-time state.

Draw() → void · abstract

Runs once per frame while IsLooping is true. The only member every sketch must implement.

Size(int w, int h)

Sets the canvas dimensions. Call from Setup().

FrameRate(int fps)

Sets the target frame rate. Minimum 1.

NoLoop() / Loop()

Stops or resumes automatic per-frame redraw via IsLooping.

Delay(int ms)

Halts the calling thread for ms milliseconds. Since Draw() runs on whatever thread the host pumps its render loop on, a long Delay() blocks that same thread/frame — exactly like Processing blocking its own animation thread.

Not for animation pacing. Use FrameRate() for smooth motion. Delay() is meant for sketches that only need to redraw occasionally and would rather sit idle than burn CPU on frames nobody will see change — a slow-updating clock, a sensor readout, an ambient display.

Thread(string methodName)

Runs a no-argument method of this sketch on a background thread and returns immediately, like Processing's thread(functionName). Looks the method up by name via reflection (public or private, instance method) — same string-based API as Processing's own.

No automatic locking. Write results to a field the background method owns, and have Draw() just read that field each frame. Keep the handoff to something simple (a single field write/read, or your own lock) to avoid tearing.

WindowResized() → void · virtual

Called when the canvas size changes after the first frame.

DisplayDensity() → int · PixelDensity(int density)

HiDPI helpers kept for API parity. DisplayDensity() always returns 1 — DanaProcessing draws everything at logical-pixel resolution and leaves any HiDPI scaling to the host. PixelDensity() only validates its argument (1 or 2, like Processing) and otherwise has no effect.

PixelWidth · PixelHeight → int

Width/height of the sketch's backing buffer in actual pixels, like Processing's pixelWidth/pixelHeight. Since DanaProcessing always draws at 1:1 (see PixelDensity() above), these are always equal to Width/Height — kept so sketches ported from Processing that read them directly (e.g. when indexing LoadPixels()'s array by hand) keep working unchanged.

Settings() → void · virtual

Runs once, before Setup(), like Processing's settings(). The host is responsible for calling it immediately before Setup().

Optional in C#. Real Processing needs this only because size() is special-cased by the preprocessor and can't take a variable inside setup(). Plain C# has no such restriction — Size() works fine called directly inside Setup() too. Overriding Settings() is provided purely so sketches ported from Processing that rely on the setup-order guarantee (settings() strictly before setup()) keep behaving the same way.

FullScreen(bool fullScreen = true) · WindowMove(x, y) · WindowResize(w, h) · WindowResizable(bool) · WindowTitle(string) · WindowRatio(w, h)

Window-management requests, each backed by an event a host can subscribe to and act on — the same pattern RedrawRequested/ExitRequested already use. SetLocation(), SetResizable(), and SetTitle() are classic-API aliases for WindowMove(), WindowResizable(), and WindowTitle() respectively.

callevent raisedreadable state
FullScreen(bool)FullScreenRequestedIsFullScreen
WindowMove(x, y)WindowMoveRequested—
WindowResize(w, h)WindowResizeRequested—
WindowResizable(bool)WindowResizableChangedIsWindowResizable
WindowTitle(string)WindowTitleChangedWindowTitleText
WindowRatio(w, h)WindowRatioRequested—
Host-dependent. Calling any of these always updates the readable state and raises its event — that part works regardless of host. Whether the actual OS window visibly moves/resizes/retitles depends entirely on whether the host you're running in (the bundled IDE, a custom Avalonia/WPF shell) subscribes to that event and acts on it. A host that doesn't wire these up yet will accept the calls silently rather than throwing.

Redraw() → void

Requests a single extra frame while NoLoop() is in effect, raising the RedrawRequested event. Looping sketches don't need this — they're already rendering continuously.

Exit() → void

Raises the ExitRequested event. DanaProcessing doesn't own a process to terminate — whatever hosts the sketch (a window, a canvas control) should subscribe and close/stop accordingly.

Print(object message) · Println(message) · LogWarning(message) · LogError(message)

Print writes with no trailing newline and no timestamp/coloring — meant for building a line piece by piece across calls, finished off with a Println. Println/LogWarning/LogError go through DanaLogger, timestamped and color-coded by level.

propertytypemeaning
FrameCountintFrames drawn so far.
Width / HeightintCurrent canvas size.
TargetFrameRateintSet via FrameRate().
IsLoopingboolWhether the host should keep calling Draw().
DisplayWidth / DisplayHeightintSize of the screen the sketch runs on. 0 until the host sets it.
03 · Drawing

Color

Fill and stroke are persistent state, set before the shape that uses them. All three-argument color methods are interpreted through the current ColorMode — RGB (0–255) or HSB (hue 0–360, saturation/brightness 0–100).

ColorMode(ColorSpaceMode mode) · ColorMode(mode, max) · ColorMode(mode, max1, max2, max3, maxA?)

Chooses how Fill/Stroke/Background/Color interpret arguments. With no range given, both RGB and HSB default to 0–255 on every channel — same as Processing, HSB doesn't silently switch to 360/100/100 until you ask for it explicitly, e.g. ColorMode(HSB, 360, 100, 100). The 3-max overload leaves alpha's range untouched.

Color(a1, a2, a3, alpha = 255) · Color(gray, alpha = 255) → Color

Builds a Color value, interpreted through the current ColorMode — the direct equivalent of Processing's color(...) function. Different from new Color(r, g, b), which is always plain 0–255 RGB no matter what ColorMode is set to.

Why both can coexist. Color the struct can't declare a same-named method on itself (C# error CS0542), so new Color(r, g, b) is the constructor form. GraphicsContext.Color(...) is a different type entirely, so it's free to use the name — and C# never confuses the two: new Color(...) always means the constructor, bare Color(...) always means this method.

Fill(a1, a2, a3, alpha = 255) · Fill(gray, alpha = 255) · Fill(Color c) · FillHSB(...) · NoFill()

Sets the fill color. FillHSB always reads as HSB regardless of ColorMode.

Stroke(a1, a2, a3, alpha = 255) · Stroke(gray, alpha = 255) · Stroke(Color c) · StrokeHSB(...) · NoStroke()

Sets the stroke (outline) color.

StrokeWeight(float w) · StrokeCap(StrokeCapKind) · StrokeJoin(StrokeJoinKind)

Line width, end-cap style, and corner style.

Naming collision. StrokeCapKind.Square maps to Skia's Butt (flush), and StrokeCapKind.Project maps to Skia's Square (extends past the endpoint).

Background(a1, a2, a3, alpha = 255) · Background(gray, alpha = 255) · Background(Color c) · Clear()

Background fills the canvas with an opaque color. Clear wipes to transparent — useful on PGraphics.

Tint(a1, a2, a3, alpha = 255) · Tint(byte alpha) · NoTint()

Sets a color/alpha multiplier applied to images drawn via Image().

Red / Green / Blue / Alpha / Hue / Saturation / Brightness(Color c)

Extracts one channel from a Color.

LerpColor(Color c1, Color c2, float amt) → Color

Interpolates two colors in RGB space.

struct Color

An RGBA value — a real struct built with new Color(r, g, b, a) or Color.FromHsb(h, s, br, a).

PushStyle() · PopStyle()

Saves/restores every drawing style this class tracks — fill/stroke colors, ColorMode ranges, RectMode/EllipseMode/ShapeMode/ImageMode, tint, stroke weight/cap/join, text size/align/leading, curve tightness, Smooth, and BlendMode — deliberately everything PushMatrix/PopMatrix doesn't already cover.

Not restored. A gradient set via LinearGradientFill/RadialGradientFill isn't tracked by PushStyle — call Fill() with a plain color afterward if you need to clear one.

Smooth() · NoSmooth()

Toggles antialiasing for shapes, lines, and text drawn from here on. On by default; NoSmooth() gives hard, pixelated edges — occasionally wanted for pixel-art-style sketches.

BlendMode(BlendModeKind mode)

Sets the compositing mode for every drawing operation from here on — shapes, images, and text. See BlendModeKind for the full list, and Pixels for Blend(), which takes a mode per call instead of setting it globally.

04 · Drawing

2D primitives

Rect, ellipse, arc, and custom-shape drawing all share the same four-argument alignment convention, resolved by ShapeAlignMode.

RectMode / EllipseMode / ShapeMode(ShapeAlignMode mode)

Sets how the four arguments are interpreted. Default is Corner for rect and shape, Center for ellipse.

modea, b, c, d mean
Cornerx, y, width, height
Cornersx1, y1, x2, y2 (opposite corners)
Centercenter x, center y, width, height
Radiuscenter x, center y, x-radius, y-radius

Rect(a, b, c, d) · Ellipse(a, b, c, d)

Draws per the active mode, filled and/or stroked.

Square(x, y, extent)

A Rect with equal width and height — a plain alias, still subject to the current RectMode().

Circle(x, y, extent)

An Ellipse with equal width and height — a plain alias for Ellipse(x, y, extent, extent), still subject to the current EllipseMode().

Line(x1, y1, x2, y2) · Point(x, y)

A straight stroked segment, or a single point.

Triangle(x1,y1, x2,y2, x3,y3) · Polygon(...) · Quad(...)

Polygon closes and fills/strokes an arbitrary point list.

Arc(a, b, c, d, start, stop, mode = ArcMode.Open)

Draws an arc of the ellipse bounded per EllipseMode.

ArcMode
OpenJust the curved arc, unconnected.
ChordArc closed with a straight line between ends.
PieArc closed through the center — a wedge.

Bezier(x1,y1, cx1,cy1, cx2,cy2, x2,y2)

Draws a cubic Bézier curve, stroked only.

BezierPoint(...) · BezierTangent(...) → float

Evaluate one axis of a cubic Bézier at parameter t.

Curve(x1,y1, x2,y2, x3,y3, x4,y4) · CurveTightness(float t)

A Catmull-Rom curve through four points.

CurvePoint(...) · CurveTangent(...) → float

Evaluate one axis of a Catmull-Rom curve at parameter t, the way Curve()/CurveVertex() shape it. Always uses the standard tension — unlike Curve() itself, this doesn't read CurveTightness(), matching Processing's own curvePoint().

BezierDetail(int detail) · CurveDetail(int detail)

Stored for API parity with sketches ported from Processing. Skia already draws exact, analytically-flattened Bézier/Catmull-Rom curves regardless of segment count, so neither has any visible effect here.

05 · Drawing

Custom shapes & meshes

Build arbitrary shapes vertex by vertex. There are two render paths at EndShape(): a simple path when every vertex shares one fill color, and a mesh path (Gouraud shading and/or texturing) once either varies.

BeginShape(ShapeKind kind = Polygon)

Starts recording vertices. See ShapeKind for all interpretations.

Vertex(x, y) · Vertex(x, y, u, v)

Adds a vertex, colored by the current Fill() at the moment of the call. The UV overload adds texture coordinates.

BezierVertex(...) · QuadraticVertex(...)

Adds a curved segment to a Polygon-mode shape.

CurveVertex(x, y)

Adds a Catmull-Rom curve vertex, respecting CurveTightness(). As in Processing, the curve only passes through the interior points — the very first and very last CurveVertex() calls shape the tangent at each end but aren't drawn to themselves, so nothing appears until the 4th call. The common pattern is to repeat the first and last point once so the curve visibly starts/ends there.

BeginContour() · EndContour()

Cuts a hole in the shape currently being built — the vertices recorded between these two calls form a subpath that's subtracted from the outline, like a donut. Only valid in the default Polygon mode of BeginShape().

Only contour shapes are affected. Using BeginContour() switches that shape's fill rule to even-odd so the hole punches through reliably regardless of winding direction. Shapes that never call it keep the usual winding rule and render exactly as before.

Texture(PImage img) · NoTexture()

Sets or clears the texture image for the shape.

TextureMode(TextureModeKind mode) · TextureWrap(TextureWrapKind mode)

Sets how Vertex(x, y, u, v)'s u/v are interpreted, and how a texture samples outside its 0–1 range. See TextureModeKind / TextureWrapKind.

Default differs from Processing. DanaProcessing's default is Normal (0–1 UVs), not Processing's own default of Image (pixel-coordinate UVs) — Vertex(x, y, u, v) already treated its u/v as 0–1 before TextureMode() existed, so the default was kept for backward compatibility. Call TextureMode(Image) for Processing's real default behavior.

EndShape(bool close = false)

Finishes and draws the recorded shape.

00 · 3D — Renderer3D

Getting started with 3D

An entirely separate rendering pipeline — offscreen OpenGL 3.3 (core profile) via Silk.NET, rendered into a hidden window's framebuffer and read back as a normal PGraphics. Everything under this "3D" nav group requires RendererKind.Renderer3D and throws InvalidOperationException otherwise — the same way Processing itself errors when box()/rotateZ()/etc. are called under the default 2D renderer.

Size(w, h, RendererKind.Renderer3D) · CreateGraphics(w, h, RendererKind.Renderer3D)

Requests the 3D pipeline for the main sketch canvas, or for an offscreen buffer. The renderer is fixed for that context's entire lifetime — calling either again with a different RendererKind later throws, matching Processing's own restriction that size(w, h, renderer) can't switch renderers mid-sketch.

Set it directly on the main canvas — compositing is automatic. Pass RendererKind.Renderer3D straight to the sketch's own Size() in Setup(). Sketch.RenderFrame() already runs your 3D draw calls against the GPU backend and composites the result with any 2D overlay — Fill()/Text() calls after your 3D geometry — automatically, every frame. Every built-in 3D example uses this direct form; a separate offscreen 3D PGraphics via CreateGraphics() is still there for when you actually want one (a texture rendered once in Setup(), say), just not as the default.

BeginDraw() / EndDraw()

Only needed for an offscreen 3D PGraphics (from CreateGraphics()) — the main sketch canvas never needs them, since RenderFrame() calls the GPU backend's begin/end for you. An offscreen 3D buffer only accepts drawing calls between these two — Box(), Sphere(), Lights(), custom Vertex()/EndShape(), all of it — and throws InvalidOperationException outside the bracket.

Exception: CreateShape3D(). Building a reusable mesh doesn't need the bracket — it claims and releases the GL context on its own, so it's safe to call once, loose, in Setup(). See Custom 3D shapes.
public class MySketch : Sketch
{
    public override void Setup() => Size(700, 500, RendererKind.Renderer3D);

    public override void Draw()
    {
        Background(20);
        Lights();                              // every frame — see Lights()
        Fill(160, 200, 255);
        PushMatrix();
        Translate(Width / 2f, Height / 2f, 0);
        RotateY(FrameCount / 60f);
        Box(160);
        PopMatrix();
    }
}
00 · 3D — Renderer3D

3D Primitives

Two built-in solids — the same two Processing itself ships as core 3D primitives. Both are centered on the current origin, filled with the current Fill() color, and shaded by the active lights (flat and fully unlit if Lights() hasn't been called — Processing's own default too).

Box(float size) · Box(w, h, d)

A cube (or box) centered on the origin. Box(size) is a shortcut for Box(size, size, size).

Sphere(float radius)

A sphere centered on the origin, meshed at the resolution set by the last SphereDetail() call (default 30×30).

SphereDetail(int res) · SphereDetail(ures, vres)

Sets the sphere mesh resolution for Sphere() calls made after this one — res segments both around the equator and pole-to-pole, or independently via ures/vres. Values below 3 are clamped up to 3. Applied lazily: the mesh is only rebuilt the next time Sphere() actually runs, so calling this repeatedly (or never calling Sphere() at all) never touches the GPU for nothing.

00 · 3D — Renderer3D

3D Transformations

PushMatrix()/PopMatrix() (from Transformations) work exactly as before, and now also save/restore the 3D model matrix alongside the 2D one. Everything below composes the usual way: each call prepends to the current matrix, so Translate() then RotateX() rotates around the already-translated origin, not the world origin.

Translate(x, y, float z)

3D translate. The 2D Translate(x, y) overload still works under Renderer3D too — z is implicitly 0, matching Processing.

RotateX(radians) · RotateY(radians) · RotateZ(radians)

Rotates around each axis. Note these take radians — unlike the 2D Rotate(degrees), which deliberately takes degrees. Under Renderer3D both exist and compose: Rotate() rotates the Skia 2D overlay, RotateZ() rotates 3D geometry — they're genuinely different operations.

Scale(x, y, float z)

3D scale.

Rotate(float angleRadians, x, y, z)

Rotates by angleRadians around the arbitrary axis (x, y, z) — the P3D-only overload of rotate(). Takes radians, like RotateX/Y/Z() (not the 2D Rotate(degrees)). The axis doesn't need to be pre-normalized; a zero-length axis is a no-op.

One combined rotation beats three stacked ones. RotateX()+RotateY()+RotateZ() compose differently depending on call order, and can twist in unexpected ways once an object is no longer near its rest orientation (the classic gimbal-lock feel). A single axis-angle Rotate() — typically derived from an accumulated quaternion for a trackball-style drag — sidesteps that entirely. See the "Gema facetada" sample's ApplySpin() helper for the pattern.
00 · 3D — Renderer3D

Camera & Projection

Two independent matrices: the camera (view — where the eye is and what it's looking at) and the projection (perspective vs. orthographic, field of view, clipping planes). Both start at Processing's own defaults and stay set until changed.

Camera() · Camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ)

No-arg resets to Processing's default view — eye pulled back from the canvas center, looking straight at it, +Y up. The 9-argument form positions the eye explicitly, looking at center, with up as the up direction.

Perspective() · Perspective(fovy, aspect, zNear, zFar)

No-arg resets to the default perspective projection (60° vertical FOV). The 4-argument form sets an explicit symmetric perspective — fovy in radians, aspect = width/height.

Ortho() · Ortho(left, right, bottom, top) · Ortho(left, right, bottom, top, near, far)

Orthographic (parallel) projection — objects stay the same size regardless of distance from the camera, useful for isometric-style views. No-arg uses the canvas bounds; the 4-argument form sets the X/Y clipping volume with default near/far planes; the 6-argument form sets everything explicitly.

Frustum(left, right, bottom, top, near, far)

A general (possibly asymmetric) perspective projection from explicit clipping-plane coordinates — unlike Perspective(), the planes don't need to be centered on the view axis. near must be greater than zero, far greater than near. Processing gives frustum() no no-argument form, so neither does this.

BeginCamera() · EndCamera()

Places the camera with Translate()/RotateX/Y/Z()/Scale() — the same calls, composed the same way, you'd use to place any object — between these two. EndCamera() inverts the result into the actual view matrix once.

Call Camera() first. BeginCamera() continues from whatever camera state already exists rather than resetting it — start from a known placement with Camera() (or the 9-argument overload) right before, same idiom as real Processing.

PrintCamera() · PrintProjection()

Logs the current view or projection matrix to the console — a debugging aid for figuring out what a chain of camera calls actually produced.

00 · 3D — Renderer3D

Lights

Up to 8 lights — Processing's own cap — combined with a Blinn-Phong shading model. Without any light call, Box()/Sphere() and custom 3D meshes draw flat and fully unlit, ignoring normals entirely, which is Processing's own documented default too.

The light list resets every frame. BeginDraw() clears all active lights, so Lights()/PointLight()/etc. need to be called again every Draw() to stay active — not just once in Setup(). This matches Processing's own documented requirement exactly. Lights past the 8-light cap are silently ignored, not an error.

Lights() · NoLights()

Lights() turns on Processing's default rig — a mid-gray ambient light plus a mid-gray directional light shining straight along -Z — with LightFalloff(1,0,0) (no falloff) and LightSpecular(0,0,0) (no specular contribution). NoLights() clears everything back to flat/unlit.

AmbientLight(r, g, b) · AmbientLight(r, g, b, x, y, z)

Uniform light that lights every surface regardless of orientation. The no-position overload is infinite and unattenuated; the positioned overload falls off with distance according to the current LightFalloff().

DirectionalLight(r, g, b, nx, ny, nz)

Shines from a fixed direction with no position and no distance falloff — think sunlight. (nx, ny, nz) is the direction the light travels from, matching Processing's own convention.

PointLight(r, g, b, x, y, z)

Radiates equally in all directions from a position, attenuated by the current LightFalloff() with distance.

SpotLight(r, g, b, x, y, z, nx, ny, nz, angle, concentration)

A cone of light at (x, y, z), aimed along (nx, ny, nz). angle (radians) is the half-angle of the cone; concentration controls how sharply intensity falls off toward the cone's edge (higher = tighter hotspot). Also attenuated by LightFalloff() with distance.

LightFalloff(constant, linear, quadratic) · LightSpecular(r, g, b)

Style state affecting lights added after the call — like Fill()/Stroke(), not retroactive. LightFalloff() sets the distance-attenuation coefficients for point/spot/positional-ambient lights (Processing's own default: (1, 0, 0) — no falloff at all). LightSpecular() sets the color used for specular highlights and has no visible effect until Shininess() is above 0 — see Material properties.

00 · 3D — Renderer3D

Material properties

Unlike the light list, these are persistent style state — set once, applied to every shape drawn afterward, not reset per frame — the same contract as Fill()/Stroke().

Ambient(gray) · Ambient(r, g, b)

Ambient reflectance — how much of each light's ambient contribution a surface reflects. Before this is ever called, it defaults to tracking the current Fill() color, matching Processing; calling it explicitly detaches from that tracking.

Specular(gray) · Specular(r, g, b)

Specular reflectance color — controls the color of specular highlights. Has no visible effect until Shininess() is set above 0.

Emissive(gray) · Emissive(r, g, b)

Added on top of a surface's shading regardless of lighting — makes it look like it's glowing on its own, without actually casting light onto other shapes.

Shininess(float shine)

The specular exponent. 0 (the default) disables the specular highlight entirely; higher values give a smaller, glossier highlight. Needs Specular() and an active light with LightSpecular() set to be visible at all.

00 · 3D — Renderer3D

Coordinates

Query where a local point ends up, after the current transform stack — in world space, or projected all the way to screen pixels.

ModelX(x, y, z) · ModelY(x, y, z) · ModelZ(x, y, z) → float

The coordinate of (x, y, z) after applying the current model and camera transforms. Typical use: Translate()/RotateX/Y/Z() into position, call ModelX/Y/Z(0, 0, 0) to record that world position, then PopMatrix() and Translate() straight to it later — without repeating the whole rotation chain.

ScreenX(x, y, z) · ScreenY(x, y, z) · ScreenZ(x, y, z) → float

Projects (x, y, z) all the way to screen space — ScreenX/ScreenY come back in pixels (same convention as MouseX/MouseY); ScreenZ comes back as normalized device depth in [0, 1] (0 at the near plane, 1 at the far plane) — DanaProcessing's own convention, since there's no PMatrix3D here to delegate to for Processing's exact screenZ() formula. A common use: anchor a 2D label (drawn with the plain 2D Text()) to a moving 3D object.

00 · 3D — Renderer3D

Custom 3D shapes

BeginShape()/Vertex()/EndShape() (from Custom shapes) work under Renderer3D too — they build and draw a real triangle mesh instead of an SKPath. Triangulation happens on the CPU; the result uploads as one GL_TRIANGLES draw call, the same way Box()/Sphere() already work.

Normal(float nx, ny, nz)

Sets the current normal vector. Affects vertices added after this call — call it once per face, before that face's vertices, for flat shading (the faceted-gem example below), or share one normal across several vertices for smooth shading. Default before any call: (0, 0, 1).

Vertex(x, y, float z) · Vertex(x, y, z, u, v)

3D vertex overloads of Vertex(), only valid inside BeginShape()/EndShape() while running under Renderer3D — the plain Vertex(x, y) also works there, treated as z = 0. The 5-argument overload adds texture coordinates (u, v) in 0–1, looked up in whatever image Texture() set for this shape — the same Texture()/NoTexture() the 2D mesh path already uses.

ShapeKind under Renderer3Dtriangulation
TrianglesConsecutive groups of 3.
TriangleStripShared-edge strip, winding alternates by parity.
TriangleFanAll triangles share vertex 0.
QuadsGroups of 4, split into 2 triangles each.
QuadStripShared-edge quad strip, split into 2 triangles each.
PolygonFan-triangulated from vertex 0 — correct for convex shapes, same limitation Processing itself documents for concave ones under P3D.
Points / LinesNot supported — no meaningful normal to light them with. Throws NotSupportedException.
// Faceted octahedron -- each face calls Normal() (via PVector.Cross()
// between two edges) before its 3 Vertex() calls, for flat shading.
BeginShape(ShapeKind.Triangles);
var faceNormal = PVector.Cross(PVector.Sub(b, a), PVector.Sub(c, a)).Normalize();
Normal(faceNormal.X, faceNormal.Y, faceNormal.Z);
Vertex(a.X, a.Y, a.Z);
Vertex(b.X, b.Y, b.Z);
Vertex(c.X, c.Y, c.Z);
// ... repeat per face ...
EndShape();

CreateShape3D(ShapeKind kind, Action buildVertices) → PShape

Builds a reusable 3D PShape once — like combining Processing's createShape() with its own beginShape()/endShape(). Call the same Vertex()/Normal() calls you'd use with immediate-mode BeginShape()/EndShape() inside buildVertices — don't call BeginShape()/EndShape() yourself in there, CreateShape3D() wraps both. The mesh uploads to the GPU exactly once (StaticDraw); draw it as many times as you want afterward with Shape(shape, x, y) instead of re-recording and re-uploading the same geometry every frame the way a bare BeginShape()/EndShape() call would.

No BeginDraw()/EndDraw() needed. This claims and releases the GL context on its own — call it loose, typically once in Setup().
Texture() goes inside the callback. BeginShape() (called internally by CreateShape3D(), before buildVertices runs) resets the shape's texture — so call Texture(img) as the first line inside buildVertices, not before CreateShape3D().
_gemShape = CreateShape3D(ShapeKind.Triangles, () =>
{
    // ... Normal()/Vertex() per face, as above ...
});

// every frame, in Draw():
PushMatrix();
Translate(x, y, z);
Shape(_gemShape, 0, 0);   // no re-triangulating or re-uploading anything
PopMatrix();
Scaling. Unlike the 2D Shape() overload, w/h scaling isn't supported for a 3D mesh — a baked mesh's own coordinates already define its size. Wrap the call in your own PushMatrix()/Scale()/Rotate()/PopMatrix() instead.
00 · 3D — Renderer3D

PShader

Swap DanaProcessing's built-in Blinn-Phong shader for a custom GLSL program, applied to every subsequent Box()/Sphere()/custom-mesh draw. One program replaces the whole 3D pipeline at once — there's no per-PShaderFlag slot (POINTS/LINES/etc.) like real Processing.

LoadShader(string fragFilename) · LoadShader(fragFilename, vertFilename) → PShader

The single-argument overload reads a fragment shader from disk and reuses DanaProcessing's own built-in vertex shader. The two-argument overload reads both — note the parameter order matches Processing's own (fragment path first, then vertex) even though it reads backwards. Neither touches the GPU until the result is passed to Shader(); compilation happens lazily, the first time it's actually used with a given 3D context.

Fixed attribute layout. A custom vertex shader must declare layout(location = 0) in vec3 aPos;, layout(location = 1) in vec3 aNormal;, and (for texture support) layout(location = 2) in vec2 aUV; — the same layout Box()/Sphere()/custom meshes upload. A fragment shader that wants to sample a texture set via Texture() should declare uniform sampler2D uTexture; and uniform int uHasTexture; and check the flag — DanaProcessing sets both automatically per draw call.

Shader(PShader shader) · ResetShader()

Shader() activates a custom shader for subsequent draws, staying active across frames until ResetShader() or another Shader() call. ResetShader() reverts to the built-in pipeline.

06 · Drawing

Typography

TextSize(float size) · TextFont(string? path, float? size = null) · TextFont(PFont font, float? size = null)

TextFont(null) resets to the default typeface. The string overload loads a font file directly; the PFont overload activates a font previously obtained via CreateFont/LoadFont and reuses its size unless you override it.

CreateFont(string fontFamily, float size) → PFont

Creates a font from an installed system font family, like Processing's createFont(name, size). Unknown family names silently fall back to the system default rather than throwing.

LoadFont(string path, float size = 32) → PFont

Loads a font file (.ttf/.otf/etc.) from disk.

Different from Processing's loadFont(). Processing's own loadFont() loads a pre-baked .vlw bitmap font produced by the PDE's Tools > Create Font. DanaProcessing has no equivalent pipeline, so this loads a real font file directly instead — same practical result, one less step.

class PFont

A loaded/created font. Get one from CreateFont()/LoadFont(), then activate it with TextFont(font). Implements IDisposable — dispose it once nothing is drawing with it anymore.

TextAlign(TextAlignH h, TextAlignV v = Baseline)

Sets both axes of alignment.

TextMode(TextRenderMode mode)

Accepts Model — the only supported mode, regular text drawn straight to the canvas. Shape throws NotSupportedException, the same treatment as Filter(Erode/Dilate).

TextLeading(float leading)

Line spacing for multi-line strings.

TextWidth(string text) · TextAscent() · TextDescent() → float

TextWidth returns the widest line in a multi-line string.

Text(string text, x, y)

Draws text at (x, y) per the current alignment.

07 · Drawing

Transformations

PushMatrix() · PopMatrix()

Saves and restores the canvas's transform stack.

Translate(x, y)

Shifts the origin.

Rotate(float degrees)

Takes degrees, not radians — a deliberate deviation from Processing.

Scale(float sx, float? sy = null)

Uniform scale when sy is omitted.

ShearX(float angleRadians) · ShearY(float angleRadians)

Shears drawing along one axis.

ResetMatrix()

Replaces the current transform with the identity, discarding every Translate/Rotate/Scale/Shear/ApplyMatrix applied so far — including ones from before the innermost PushMatrix(). Stronger than PopMatrix(), which only undoes back to the last PushMatrix().

ApplyMatrix(a, b, c, d, e, f)

Multiplies the current transform by an arbitrary affine matrix [[a c e][b d f][0 0 1]] — for effects the named transform calls can't express directly.

PrintMatrix()

Prints the current transformation matrix to the console as a 3×3 row-major matrix — a debugging aid for figuring out what a chain of transform calls actually produced.

Clip(x, y, w, h) · NoClip()

Restricts drawing to a rectangle until NoClip() is called — or until Clip() is called again with a new rectangle, which replaces rather than intersects the old one. Always uses corner-style x/y/w/h regardless of the current RectMode().

Don't straddle PushMatrix/PopMatrix. Implemented with its own internal save/restore pair on the canvas — pair a Clip() with its NoClip() at the same matrix-nesting depth, rather than spanning a PushMatrix()/PopMatrix() boundary.

Hint(string hintName)

A logged no-op. Every hint constant Processing defines is either meaningless for a single always-antialiased 2D software renderer, or already covered by a dedicated method (Smooth()/NoSmooth()). Exists so ported sketches that defensively call hint(SOME_CONSTANT) still compile and run.

08 · Media

PImage

A loaded bitmap. Load one with Sketch.LoadImage(path), then draw it with Image().

LoadImage(string path) → PImage

Decodes an image file from disk.

RequestImage(string path) → PImage

Starts loading an image on a background thread and returns immediately. The returned image's IsLoaded is false (and Width/Height read as 0) until the background decode finishes, at which point it silently swaps in the real bitmap — check IsLoaded in Draw() before using it, the same way a Processing sketch checks img.width != 0.

ImageMode(ShapeAlignMode mode)

Sets how Image()'s x/y/w/h are interpreted — Corner by default (x,y is top-left, w,h is size), Corners (w,h is the opposite corner instead of a size), or Center.

Image(PImage img, x, y) · Image(img, x, y, w, h)

Draws the bitmap, respecting the current Tint() and ImageMode().

img.Mask(PImage maskImage)

Applies another image as an alpha mask, in place.

09 · Media

PGraphics

An offscreen drawing buffer with the exact same drawing API as Sketch.

CreateGraphics(int w, int h) → PGraphics

Creates a new raster-backed offscreen surface.

BeginDraw() · EndDraw()

Brackets a batch of drawing calls.

Get() → PImage

Snapshots the current pixels as a standalone PImage.

10 · Media

PShape

A loaded or built vector shape. Four ways to get one: load an SVG, build a primitive or group in code, or — since you're reading the 3D docs —under the 3D renderer — build a reusable mesh with CreateShape3D(). A given PShape is either a 2D SKPicture or a 3D mesh handle, never both.

LoadShape(string path) → PShape

Loads an SVG file.

CreateShape(PShapeType type, params float[] v) → PShape

Builds a standalone, reusable primitive shape in code, like Processing's createShape(RECT/ELLIPSE/LINE/TRIANGLE/QUAD, ...). x/y/w/h are in the shape's own local coordinate system — Shape(built, drawX, drawY) later draws it translated by (drawX, drawY), so it ends up on screen at (drawX + x, drawY + y), matching real Processing. Captures the current Fill()/Stroke()/StrokeWeight() as the shape's fixed, baked-in style — changing them afterward has no retroactive effect.

PShapeType
Rect4 values: x, y, w, h.
Ellipse4 values: x, y, w, h.
Line4 values: x1, y1, x2, y2.
Triangle6 values: x1,y1, x2,y2, x3,y3.
Quad8 values: x1,y1, x2,y2, x3,y3, x4,y4.

CreateShape(params PShape[] children) → PShape

Groups several 2D shapes into one, like Processing's createShape(GROUP) followed by addChild() for each piece. Each child keeps the absolute local coordinates it was built with — build a rocket out of a triangle, a rect, and an ellipse once in Setup(), then stamp the whole group with a single Shape() call per instance in Draw().

2D only. Group a set of CreateShape3D() meshes by drawing each with its own Shape() call instead — there's no 3D-mesh grouping yet.

Shape(PShape shape, x, y) · Shape(shape, x, y, w, h)

Draws the shape, positioned/sized per the current ShapeMode. Dispatches automatically to the 3D draw path when shape wraps a mesh from CreateShape3D() — see Custom 3D shapes.

Tint doesn't apply here. To tint a vector shape, render it into a PGraphics first.
11 · Media

Saving output

Save(string path, int quality = 100)

Encodes the current canvas to disk. Format is chosen from the file extension — .png, .jpg/.jpeg, .bmp, or .webp.

SaveFrame(string pattern = "screen-####.png")

Like Save(), but substitutes # with FrameCount.

BeginRaw(string path) · EndRaw() · BeginRecord(string renderer, path) · EndRecord()

Exports every draw call between BeginRaw()/EndRaw() to a vector PDF instead of the screen. BeginRecord()/EndRecord() are plain aliases — renderer is accepted for API parity with Processing but ignored, since only PDF export exists here.

Redirects, doesn't fork. Real Processing draws to the screen and records at the same time. DanaProcessing instead redirects every draw call to the PDF page for the duration of the block — nothing appears on screen while a recording is active. The idiomatic pattern: factor your actual drawing into its own method, and call it twice — once normally in Draw(), once more (typically on a keypress, not every frame) wrapped in BeginRaw()/EndRaw().
private void DrawPoster() { /* Background/Fill/Circle/... */ }

public override void KeyPressed()
{
    if (Key == 'v')
    {
        BeginRaw("poster.pdf");
        DrawPoster();
        EndRaw();
    }
}
12 · Media

Pixels

Direct pixel access and per-pixel operations. Reading pixels back (LoadPixels, Get, and everything built on them) requires a CPU-readable Surface — the same requirement Save() has.

LoadPixels() · Pixels → Color[] · UpdatePixels()

LoadPixels() snapshots the canvas into the Pixels array (row-major, index = y * Width + x) for reading or editing. UpdatePixels() writes it back — verbatim, no blending with what was there. Pixels throws if LoadPixels() hasn't been called; it deliberately doesn't auto-load, so it's always clear whether you're looking at a fresh read.

Get(x, y) → Color · Get(x, y, w, h) → PImage · Get() → PImage

Reads pixels straight from the canvas — no LoadPixels() needed. Out-of-bounds coordinates come back transparent rather than throwing.

Set(x, y, Color c) · Set(x, y, PImage img)

Writes a single pixel or draws an image with pixels copied verbatim — no tint, no blending. Out-of-bounds coordinates are silently ignored.

Copy(sx,sy,sw,sh, dx,dy,dw,dh) · Copy(PImage src, sx,sy,sw,sh, dx,dy,dw,dh)

Copies a region (scaling if source/destination sizes differ) — verbatim, no blending. Without a source image, copies from this same canvas onto itself.

Blend(sx,sy,sw,sh, dx,dy,dw,dh, BlendModeKind mode) · Blend(PImage src, ...)

Same as Copy(), but composites through the given blend mode instead of overwriting. See BlendModeKind.

Filter(FilterKind kind, float param = 0.5f)

Applies a per-pixel filter to the whole canvas in place. param means different things per kind: the cutoff for Threshold (0–1), the levels per channel for Posterize, the pixel radius for Blur; ignored otherwise. See FilterKind.

Not implemented. Erode/Dilate need neighbor-pixel sampling rather than a per-pixel transform and throw NotSupportedException for now.
13 · Data & Math

Files & data

Reading and writing plain text, bytes, JSON, XML, and CSV — all on Sketch, mirroring Processing's load*/save* globals.

LoadStrings(string path) → string[] · SaveStrings(path, lines)

Reads/writes a text file as an array of lines.

LoadBytes(string path) → byte[] · SaveBytes(path, data)

Reads/writes a file's raw bytes.

LoadJSONObject(path) → JSONObject · SaveJSONObject(json, path) · LoadJSONArray(path) → JSONArray · SaveJSONArray(json, path)

Reads/writes JSON files. JSONObject/JSONArray wrap System.Text.Json with Processing's Get*/Set*/Append* naming (GetString, GetInt, GetFloat, GetBoolean, GetJSONObject, GetJSONArray, and their Set*/Append* counterparts).

ParseJSONObject(string json) → JSONObject · ParseJSONArray(json) → JSONArray · ParseXML(string xml) → XML

Parses an already-in-memory string instead of reading from a file — use these instead of LoadJSONObject/LoadJSONArray/LoadXML when the data came from somewhere other than disk: a network response, a string built by hand, text pasted into the sketch.

LoadXML(string path) → XML · SaveXML(xml, path)

Reads/writes an XML file. XML wraps System.Xml.Linq with GetString/GetInt/GetFloat attribute accessors, GetContent() for text, and GetChildren()/GetChild(name)/AddChild(name) for structure.

LoadTable(path, options = "") → Table · SaveTable(table, path)

Reads/writes a CSV file as a Table. Pass "header" for a first line naming the columns. Cell values are stored as text and parsed on demand via TableRow.GetInt/GetFloat/GetString, by index or column name.

CSV only. Processing's Table also reads TSV and its own binary .tbl format; only CSV is implemented here. The parser handles quoted fields with embedded commas, but not escaped double-quotes inside a quoted field — enough for CSV a spreadsheet actually produces, not a full RFC 4180 implementation.

CreateWriter(string path) → PrintWriter · CreateReader(path) → BufferedReader

Incremental text I/O — for a long-running log file, or a large input read a line at a time — where LoadStrings/SaveStrings already cover the one-shot case. CreateWriter creates the containing directory if needed. Remember to Flush()/Close() the PrintWriter when done.

CreateOutput(string path) · CreateInput(path) → Stream

Raw byte streams to/from a file, like Processing's createOutput()/createInput(). The caller owns the returned Stream and should dispose it when finished.

SaveStream(string path) → Stream · SaveStream(path, Stream input)

The single-argument overload is an alias for CreateOutput(), under Processing's other name for the same thing. The two-argument overload copies the entire contents of input to a new file at path and closes both — handy when you already have an input stream (from CreateInput() on another file, say) and just want it saved whole.

class PrintWriter · class BufferedReader

PrintWriter exposes Print(value)/Println(value?)/Flush()/Close(). BufferedReader exposes ReadLine(), which returns null once the file is exhausted.

PrintArray(Array array)

Prints every element on its own line, prefixed with its index in square brackets. Works with any array type, matching Processing's own printArray() (which needs a separate overload per type).

14 · Data & Math

Conversion & utility functions

Type conversion, array manipulation, and string formatting. Processing/Java needed dedicated functions for most of these because of stricter primitive typing and no operator overloading — in C# they exist mainly for 1:1 API parity with sketches ported from Processing.

Int(float value) · Int(string value) · Int(bool value) → int

Truncates a float toward zero (not rounding), parses a string (0 on failure), or converts true/false to 1/0.

Float(int value) · Float(string value) → float

Converts an int, or parses a string — float.NaN on failure, matching Processing's own documented behavior.

Boolean(float value) · Boolean(string value) → bool

Nonzero is true for numbers; a case-insensitive "true" for strings, anything else false.

Byte(int value) · Byte(float value) · Byte(char value) → byte

Truncates/wraps into a byte using two's-complement, matching Java/Processing's own byte() rather than throwing on overflow.

Char(int value) · Char(byte value) → char

Converts a numeric code point to its character — Char(65) is 'A'.

Str(int) · Str(float) · Str(bool) · Str(char) → string

Renders a value as text. Exists mainly so ported sketch code compiles unchanged — C#'s own ToString() already covers every case here.

Hex(int) · Hex(int, digits) · Hex(byte) · Hex(char) → string

Hexadecimal string, zero-padded — 8 digits for int by default (or a custom digit count), 2 for byte, 4 for char.

Unhex(string value) → int

Parses a hex string back into an int. Ignores an optional leading "0x"; returns 0 if the text isn't valid hex.

Binary(int) · Binary(int, digits) · Binary(byte) · Binary(char) → string

Binary string, zero-padded — 32 digits for int by default (or the rightmost digits bits), 8 for byte, 16 for char. Negative int values render their full two's-complement bit pattern, matching Processing/Java.

Unbinary(string value) → int

Parses a binary string back into an int. Anything other than '0'/'1' in the string makes the whole result 0; strings longer than 32 bits keep only the rightmost 32, matching int's width.

Prefer a plain C# cast. These exist purely for parity with sketches ported line-for-line from Processing. New DanaProcessing code can just write (int)value, value.ToString(), etc. directly.

Append<T>(array, value) · Concat<T>(a, b) · Expand<T>(array, newSize?) → T[]

Grows an array without mutating the original. Expand called with no size doubles the array (or grows an empty one to size 1) and never shrinks — matching Processing's own expand().

Reverse<T>(array) · Shorten<T>(array) · Subset<T>(array, start, count?) → T[]

Returns a new, reordered or trimmed array. None of the array functions on this page mutate their input — the result is always a fresh array, matching Processing's own contract.

Sort<T>(array) · Sort<T>(array, count) → T[]

Returns a new, ascending-sorted copy using T's natural ordering. The two-argument overload sorts only the first count elements.

Splice<T>(array, value, index) · Splice<T>(array, insertion, index) → T[]

Inserts a single value, or every element of another array, starting at index.

ArrayCopy<T>(src, dst) · ArrayCopy<T>(src, srcPos, dst, dstPos, count)

Copies elements into an already-sized destination array — the one function here that writes into an existing array rather than returning a new one, matching Processing's own arrayCopy().

Generic, not per-type. Processing needs a separate overload of each array function per primitive type (Java has no clean generics-over-arrays story). DanaProcessing needs just one <T> implementation, covering int[], float[], string[], PVector[], or anything else.

Join(array, separator) → string · Split(value, delimiter) → string[]

Split takes either a single char or a (possibly multi-character) string delimiter, matched literally.

SplitTokens(value) · SplitTokens(value, delimiters) → string[]

Splits on runs of whitespace, or on runs of any character found in delimiters (each character is its own delimiter, not a multi-character sequence). Empty tokens between adjacent delimiters are dropped.

Trim(string) · Trim(string[])

Trims leading/trailing whitespace — a single string, or every element of an array.

Match(value, regexp) → string[]? · MatchAll(value, regexp) → string[][]?

Regular-expression matching. Match returns the first match's groups (index 0 is the whole match); MatchAll returns one row of groups per match, non-overlapping. Both return null rather than an empty array when there's no match at all.

Nf(num, digits) · Nf(num, left, right) → string

Zero-pads a number to a fixed digit count — an int overload, and a float overload with separate widths for the digits before and after the decimal point.

Nfc(num) · Nfc(num, right) → string

Same idea as Nf, with thousands separators added.

Nfp(num, digits) · Nfp(num, left, right) → string

Same as Nf, but always shows a leading + or - sign.

Nfs(num, digits) · Nfs(num, left, right) → string

Same as Nf, but pads with spaces instead of zeros.

15 · Data & Math

Math & trig

Free functions on Sketch, mirroring Processing's globals. All angles are radians, except Rotate().

constantvalue
PI / TWO_PI / TAUπ, 2π, 2π (alias)
HALF_PI / QUARTER_PIπ/2, π/4
functiondoes
Sin / Cos / Tan / Asin / Acos / Atan / Atan2Standard trig
Degrees(rad) / Radians(deg)Angle unit conversion
Constrain(v, min, max)Clamps a float or int
Dist(...)Distance between points
Mag(x, y) / Mag(x, y, z)Distance from the origin — the free-function form of PVector.Mag()
Lerp(start, stop, amt)Linear interpolation
Norm(value, start, stop)Normalizes into [0, 1]
Map(value, s1, e1, s2, e2)Remaps a value
Sq / Sqrt / Abs / Ceil / Floor / Round / Pow / Log / ExpScalar math
Max(...) / Min(...)Two-argument or params-array
16 · Data & Math

PVector

A 3D vector struct for position, velocity, and acceleration — position, velocity, and acceleration in a particle system, steering, or plain 3D math for custom Vertex()/Normal() geometry. Z defaults to 0 via the 2-argument constructor, so 2D-only sketch code that only ever wrote new PVector(x, y) keeps working unchanged.

instance (mutates)static (pure)
Add / Sub / Mult / DivAdd / Sub / Mult / Div
Normalize / Limit(max) / SetMag(mag)Dist(a, b) / Dot(a, b)
Rotate(rad)† / Set(x, y, z=0)FromAngle(rad)†
Lerp(v, amt)Lerp(a, b, amt)
Cross(v)Cross(a, b)
Mag() / MagSq() / Heading()† / Copy() / Array()AngleBetween(a, b)

†Rotate()/FromAngle()/Heading() only make sense for a vector lying in the XY plane and ignore Z, matching Processing's own documented 2D-only behavior for these three. For a genuine 3D rotation, rotate the point with RotateX/Y/Z() instead.

Cross(PVector v) → PVector

The 3D cross product — perpendicular to both vectors, following the right-hand rule. Does not mutate this (unlike most other instance methods here), matching Processing's own cross(), which always returns a new PVector. For two vectors lying in the XY plane (Z = 0), the result points purely along Z — exactly what you want for a 2D face's "up" normal, as in the faceted-gem example under Custom 3D shapes.

17 · Data & Math

Random & noise

Random(float max) · Random(min, max)

Uniform random float.

RandomGaussian() → float

Normally distributed (mean 0, standard deviation 1).

RandomSeed(int seed)

Reseeds the shared random generator.

Noise(float x) · Noise(x, y) → float

Smooth pseudo-random value in [0, 1].

NoiseSeed(int seed) · NoiseDetail(int octaves, float falloff = 0.5f)

Reshuffles the noise permutation table, or changes octaves and falloff.

17.5 · Data & Math

Collections

FloatList, IntList, and StringList — Processing-flavored typed lists, like Processing's own. Thin wrappers around List<T> with the handful of methods sketches actually reach for, rather than a 1:1 port of Java's much larger surface. Standalone value-holder types — like PVector, you just new FloatList() directly, no Sketch factory method needed.

What about FloatDict/IntDict/StringDict? Not ported, on purpose. A plain C# Dictionary<string, float> (or int/string) already does exactly what Processing's dict types do, with better performance and an API every C# developer already knows. That wrapper only earned its keep in Java, which didn't have comfortable generics when Processing's API was designed.

class FloatList · class IntList · class StringList

All three share the same shape — Size, an indexer (this[int]), and the methods below. FloatList/IntList additionally expose Min()/Max()/Sum() (and FloatList alone adds Average()) — numeric summaries that don't make sense for StringList, which instead adds Join(separator). All three implement IEnumerable<T>, so a plain foreach works too.

methoddoes
Append(value)Adds to the end.
Get(i) / Set(i, value)Read/write by index (same as the indexer).
Remove(i) / Clear()Removes one element, or everything.
Contains(value) / IndexOf(value)Linear search.
Sort() / SortReverse() / Reverse()In place.
Shuffle(Random? rng = null)Fisher-Yates in place. Uses its own internal Random by default — pass your own for reproducible results tied to a seed.
ToArray()Snapshot as a plain array.

IntList.Range(int count) → IntList · static

Fills a new list with count sequential ints starting at 0 — handy for building an index list to shuffle/sort alongside parallel data.

var heights = new FloatList();
for (int i = 0; i < 24; i++)
    heights.Append(Random(40, Height - 40));

heights.Shuffle();               // reorders in place
float avg = heights.Average();  // numeric summary
18 · Interaction

Mouse, keyboard & time

Position and key state are plain properties; press/release/move are virtual methods.

mousekeyboard
MouseX / MouseY / PMouseX / PMouseYKey / KeyCode / IsKeyPressed
IsMousePressed / MouseButtonCODED, BACKSPACE, TAB, ENTER, RETURN, ESC, DELETE
MousePressed() / MouseReleased() / MouseClicked()UP, DOWN, LEFT, RIGHT, ALT, CONTROL, SHIFT
MouseDragged() / MouseMoved() / MouseWheel(delta)KeyPressed() / KeyReleased()
FocusedKeyTyped()

KeyTyped() → void · virtual

Fires for printable keys only (Key != CODED) — a companion to KeyPressed()/KeyReleased(), which fire for every key including arrows and modifiers.

Focused → bool

Whether the sketch's window currently has input focus. Defaults to true; a host that embeds the sketch updates it as its window gains/loses focus.

Millis() → long · NanoTime() → long

Millis() is milliseconds since the sketch started. NanoTime() is the same idea at nanosecond resolution — limited in practice by whatever precision the OS timer actually offers.

Day() · Month() · Year() · Hour() · Minute() · Second() → int

Reads the local system clock at call time — unlike Millis(), which is relative to sketch start.

Cursor(CursorKind kind) · NoCursor()

Raises the CursorRequested event.

SelectInput(prompt, callback) · SelectOutput(prompt, callback) · SelectFolder(prompt, callback)

Raises the SelectFileRequested event with a SelectFileRequest describing which native dialog to show. DanaProcessing doesn't own a window, so — like Cursor()/NoCursor() — this can't pop a dialog itself; the host shows its own native dialog and calls Respond(path) (or Respond(null) on cancel) once the user answers.

Launch(string path)

Opens a file, folder, URL, or application with the OS's default handler, like Processing's launch(). Fire-and-forget — returns as soon as the OS has been asked to open it. Unlike SelectInput/Cursor, this doesn't need a host at all: it goes straight to Process.Start.

01 · Sound

Sound & live coding

DanaProcessing has its own audio engine, written from scratch in C# and running inside the sketch's own process — no SuperCollider, no external synth, no extra package. It follows Sonic Pi's way of thinking: live loops that repeat in time with each other, Sleep measured in beats, built-in synths and drums. Because sound and drawing share one clock, visuals can react to the exact moment a note is heard. Everything here is on Sketch and needs no using of its own.

public class MySketch : Sketch
{
    float flash;

    public override void Setup()
    {
        Size(800, 500);
        Bpm = 120;
    }

    [LiveLoop]
    async Task Drums()
    {
        Sample("kick");
        await Sleep(1);
        Sample("kick");
        Sample("snare");
        await Sleep(1);
    }

    [LiveLoop]
    async Task Bass()
    {
        UseSynth(Synth.Saw);
        foreach (var note in new[] { "E2", "G2", "A2", "B2" })
        {
            Play(note, amp: 0.5, release: 0.3, cutoff: 80);
            await Sleep(0.5);
        }
    }

    public override void OnNote(NoteEvent e)
    {
        if (e.Sample == "kick") flash = 1;   // exactly when the kick is heard
    }

    public override void Draw()
    {
        Background(20 + flash * 60, 20, 30);
        flash *= 0.85f;
        float r = 80 + Amplitude * 600;   // grows with the volume
        Ellipse(Width / 2f, Height / 2f, r, r);
    }
}
Live coding in the IDE. While the loops play, change a note or a Sleep and press Run (Ctrl+R) or Hot Reload: the music doesn't stop. Each loop picks up its new code on its next pass, in time with the others. A loop you add joins on the next beat; a loop you delete (or whose [LiveLoop] you remove) finishes its current pass and stops. Sketch → Stop sound (Ctrl+.) silences everything. Loop errors show up in the Run tab of the bottom panel.
How the timing stays tight. Sleep doesn't pause the thread for "about" that long — it advances the loop's own musical clock by exactly that many beats. Loops run about 100 ms ahead of what you hear, and every sound is stamped with its exact sample position, so however late the thread pool wakes a loop up, the beat still lands on the exact sample. The audio thread itself never allocates memory, so garbage collection doesn't cause clicks.
Platforms. Sound output currently works on Windows (through the built-in winmm API, no install needed). On macOS and Linux the engine, live loops, OnNote and the analysis functions all run on the same clock — you just don't hear anything yet.

[LiveLoop] attribute on async Task Name()

Marks a sketch method as a live loop, like Sonic Pi's live_loop: it starts after Setup() and repeats forever, in sync with every other loop. The method must be async Task with no parameters, and every pass must await Sleep(...) at least once — a loop that finishes a pass without sleeping is stopped (and reported) instead of freezing the audio. Each pass runs the loop's code from the current version of the sketch, which is what makes Run and Hot Reload swap code without a gap.

Sleep(double beats) → Task

Waits beats beats at the current Bpm. Always await it. Inside a [LiveLoop] it's musical time — exact, never drifting, shared with every loop. Outside a loop it's a plain real-time delay.

Bpm → double · get/set

Tempo in beats per minute, shared by every loop. Default 60 (one beat = one second), like Sonic Pi. Usually set once in Setup().

UseSynth(Synth synth)

Chooses the instrument for the following Play() calls — per loop when called inside one, otherwise for the whole sketch. The default is Synth.Sine. See the Synth enumeration below.

Play(double note, …) · Play(string note, …) · Play(int[] notes, …)

Plays a note. note is a MIDI number (60 = C4, fractions allowed for microtones), a name like "C4", "F#3", "Eb2", or an array for a chord (Play(Notes.Chord("C4", "minor"))). Inside a loop the note sounds at the loop's current musical time; outside a loop (e.g. in MousePressed()) it sounds right away. All the optional parameters are named, Sonic Pi style:

parameterdefaultmeaning
amp1Volume. Several loud voices at once are softly limited, never hard-clipped.
pan0Stereo position, −1 (left) to 1 (right).
attack · decay · sustain · release0 · 0 · 0 · 1Envelope in seconds: rises over attack, falls to sustainLevel over decay, holds for sustain, fades out over release. Total length = the sum of the four.
sustainLevel1Level held during sustain, 0–1.
cutoff0 (off)Low-pass filter, as a MIDI note like Sonic Pi: 80 ≈ 830 Hz (dark), 110 ≈ 4.7 kHz (bright).
res0Filter resonance, 0–1.
synthcurrentOverrides UseSynth for this one note.

Sample(string name, double amp = 1, double rate = 1, double pan = 0) · Sample(SampleBuffer buffer, …)

Plays a sound. The built-in drums need no files: "kick" (or "bd"), "snare" ("sn") and "hat" ("hh"). Any other name is a path to a .wav file, loaded once and cached. rate changes speed and pitch together: 2 is an octave up and twice as fast, 0.5 an octave down.

LoadSample(string path) → SampleBuffer

Loads a .wav file into memory (PCM 8/16/24/32-bit or float, mono or stereo, any sample rate), cached by path. Call it in Setup() so the first Sample() doesn't have to read the disk.

Amplitude → float

How loud the sketch's sound is right now, 0–1, smoothed (rises instantly, falls gently) so it's ready to drive a size or a brightness. 0 while nothing has played.

Spectrum(float[] bands)

Fills bands with the current spectrum: bands.Length bands, log-spaced from 30 Hz (index 0) to 16 kHz, each 0–1 (0 = −60 dB, 1 = full scale). Allocate the array once as a field and reuse it every frame.

Waveform(float[] dest)

Fills dest with the latest output samples (−1 to 1, mono, oldest first) — up to 4096 of them. Handy for an oscilloscope line.

OnNote(NoteEvent e) → void · virtual

Called for every note and sample at the moment it reaches the speakers — not when the code that played it ran — on the drawing thread, right before Draw(). Override it to make visuals hit exactly on the beat. Events that pile up while drawing is paused (for instance while you type in the IDE's editor) are dropped instead of arriving in a burst.

NoteEvent record struct

Time (engine seconds), Note (MIDI number; 0 for samples), Amp, Synth, Sample ("kick", "snare", a file name… or null for notes) and Loop (the name of the [LiveLoop] method that played it, or null).

StopAudio()

Stops every live loop and silences every sound right away — the same as Sketch → Stop sound in the IDE.

Notes.Parse(string name) → int · Notes.ToFrequency(double midi) → double

Note names to MIDI numbers and MIDI numbers to Hz. Same convention as Sonic Pi: "C4" = 60, "A4" = 69 = 440 Hz. Sharps are # or s, flats b; the octave defaults to 4. A static class rather than Sketch methods because Sketch already has a Scale() — the transform one.

Notes.Chord(root, string kind = "major") → int[]

The notes of a chord built on root (a name or a MIDI number): Notes.Chord("C4", "minor") → [60, 63, 67]. Kinds: major, minor, dim, aug, 7, maj7, m7, sus2, sus4, power.

Notes.Scale(root, string kind = "major", int octaves = 1) → int[]

The notes of a scale, ending on the root an octave (or more) up, like Sonic Pi: Notes.Scale("E2", "minor_pentatonic") → [40, 43, 45, 47, 50, 52]. Kinds: major, minor, harmonic_minor, dorian, phrygian, lydian, mixolydian, major_pentatonic, minor_pentatonic, blues, chromatic.

SampleBuffer

A sound in memory, ready for Sample(). SampleBuffer.LoadWav(path) reads a file; SampleBuffer.FromMono(float[] data, int sampleRate) wraps a signal you generated in code. Read-only properties: Left, Right, Frames, SampleRate, Channels, Duration (seconds), Name. Files at a different sample rate are resampled on playback, so they always play at the right pitch.

AudioEngine.Shared → AudioEngine · advanced

The one engine every sketch shares (namespace DanaProcessing.Audio). It outlives individual sketches, which is why pressing Run doesn't cut the music, and only opens the sound device once something actually plays. Sketches rarely need it directly; the tunables are ScheduleAhead (seconds loops run ahead of what you hear, default 0.1), MasterVolume (0–1, default 0.7) and OutputDescription. Hosts call AudioEngine.ActivateSketch(sketch) after Setup() and after a hot-reload swap (the Avalonia host already does), can show AudioEngine.DiagnosticReported messages, and stop everything with AudioEngine.StopEverything().

Synth
SinePure tone — the default, like Sonic Pi's :beep.
SawBand-limited sawtooth: bright; great for bass and leads with cutoff.
SquareBand-limited square: hollow, retro.
TriangleSoft, flute-like.
PluckPlucked string (Karplus–Strong).
NoiseWhite noise — with a low cutoff, wind or waves.
Kick · Snare · HatSynthesized drums, also reachable as Sample("kick"), "snare", "hat". They ignore the note and the envelope.
19 · Reference

Enumerations

Shared between Sketch and PGraphics.

RendererKind
Renderer2DSkia software rasterizer — the default. Everything under Drawing.
Renderer3DOffscreen OpenGL 3.3. Set once via Size()/CreateGraphics() and locked for that context's lifetime. See 3D — Getting started.
ColorSpaceMode
RGBEach argument 0–255 (default).
HSBHue 0–360, saturation/brightness 0–100.
ShapeAlignMode
Cornerx, y, width, height (default for rect/shape).
CornersTwo opposite corners.
CenterCenter point, width, height (default for ellipse).
RadiusCenter point, x-radius, y-radius.
ArcMode
OpenUnconnected arc.
ChordClosed with a straight line between ends.
PieClosed through the center.
StrokeCapKind
RoundRounded line end.
SquareFlush end (Skia's "Butt").
ProjectExtends past the endpoint (Skia's "Square").
StrokeJoinKind
MiterSharp corner.
BevelFlattened corner.
RoundRounded corner.
ShapeKind
PolygonDefault — supports curved segments.
PointsEach vertex drawn as a point.
LinesVertices grouped in pairs.
TrianglesGrouped in threes; supports mesh rendering.
TriangleFan / TriangleStripShared-vertex triangles.
Quads / QuadStripGrouped or shared-vertex quads.
Under Renderer3D: Triangles/TriangleFan/TriangleStrip/Quads/QuadStrip/Polygon (triangulated as a fan) build and draw a real GPU mesh. Points/Lines aren't supported there — no meaningful normal to light them with — and throw NotSupportedException. See Custom 3D shapes.
PShapeType
Rect / Ellipse / Line / Triangle / QuadUsed with CreateShape(type, ...) to build a 2D primitive shape from raw coordinates.
TextAlignH / TextAlignV
Left, Center, RightHorizontal.
Baseline, Top, Center, BottomVertical.
MouseButtonKind / CursorKind
None, Left, Right, CenterMouse buttons.
Arrow, Cross, Hand, Move, Text, WaitCursor shapes.
BlendModeKind
BlendNormal alpha compositing (default).
AddAdditive.
Darkest / LightestPer-channel min / max.
Difference / ExclusionAbsolute difference, with/without a flattened midtone.
Multiply / ScreenDarkens / lightens by multiplying.
Overlay / HardLight / SoftLightContrast-boosting blends.
Dodge / BurnLightens / darkens the base by the blend color.
No SUBTRACT. Processing's SUBTRACT has no direct Skia blend-mode equivalent (it isn't a true channel subtraction in Skia's set), so it's deliberately left out rather than approximated poorly.
FilterKind
Gray / Invert / OpaqueGrayscale, invert RGB, force alpha to 255.
ThresholdBlack/white split at param (0–1, default 0.5).
PosterizeReduces to param levels per channel.
Blurparam is the pixel radius.
Erode / DilateNot implemented — throws NotSupportedException.
TextureModeKind
ImageVertex(x, y, u, v)'s u/v are pixel coordinates into the texture (Processing's own default).
Normalu/v are already 0–1 — DanaProcessing's default; see the note under Custom shapes.
TextureWrapKind
ClampRepeats the edge pixel outside the 0–1 range (default).
RepeatTiles the texture.
TextRenderMode
ModelRegular text drawn straight to the canvas (default, and the only mode implemented).
ShapeNot implemented — throws NotSupportedException.
20 · Hosting & Tooling

Avalonia host

Embeds a Sketch in a cross-platform Avalonia app.

AvaloniaSketchCanvas

A Control that drives the sketch's Setup()/Draw() loop on a DispatcherTimer.

Why it keeps its own surface. Avalonia's Skia interop only leases an SKCanvas, never the SKSurface — so the control keeps its own offscreen SKSurface to make Save() work.

AvaloniaSketchWindow

A thin standalone Window wrapper around AvaloniaSketchCanvas.

21 · Hosting & Tooling

The IDE

A self-contained editor + canvas window for iterating on sketches without a separate build step — not a thin wrapper around the library, but a purpose-built environment: diagnostics as you type, code navigation, an AI assistant with its own tools, packages resolved from a single comment, 35 ready-made examples, and a one-click path to a standalone app.

SketchCompiler.Compile(string sourceCode) → CompileResult

Parses the editor's text with Roslyn, compiles it in memory, and instantiates the first Sketch found — this is what pressing Run does, start to finish, well under a second for a typical sketch.

SkiaSharp stays hidden. The compiler deliberately does not reference SkiaSharp.dll — sketch code never needs to know SkiaSharp exists.

SketchEditorView

A tabbed code editor with C# syntax highlighting, live Roslyn diagnostics, and autocomplete.

Live diagnostics. Errors and warnings are computed from the same Roslyn workspace SketchCompiler uses, underlined directly in the editor as you type — no need to press Run to find a typo. Click a diagnostic in the bottom panel's "Errores en vivo" tab and the caret jumps straight to it.
Autocomplete. Completion suggestions know the full Sketch API — every method documented on this page shows up as you type, with the same signatures.

Code navigation

Standard IDE navigation, backed by the same Roslyn semantic model live diagnostics use — scoped to the one sketch you're editing, since each sketch is its own independent compilation rather than part of a larger project.

ActionShortcut
Find in fileCtrl+F
Go to DefinitionF12
Go to ImplementationCtrl+F12
Find All ReferencesShift+F12
Save (also triggers Hot Reload, see below)Ctrl+S

The three jump-to-X actions are also on the editor's right-click menu, next to Cut/Copy/Paste. References resolve within the one open sketch; a symbol from DanaProcessing itself (like Circle or PVector) is compiled with no attached source, so Go to Definition reports "no source available" for it instead of guessing at a location.

Hot Reload

Recompiles the running sketch and swaps it in without re-running Setup() or losing its current state, as long as every field still lines up (same names, same types) between the old and new version — falls back to a full restart otherwise, with the reason shown in the output panel. Triggers automatically whenever you Ctrl+S the sketch that's currently running; Sketch → Hot reload (Ctrl+Shift+R, also in the ▾ next to Run) does the same thing on demand.

Run in a separate window

Sketch → Run in a separate window (Ctrl+F5, also in the ▾ next to Run) compiles the active sketch the same way Run does, but opens the result in its own window instead of the embedded canvas — no editor, no IDE chrome around it, the way Processing's own Run button works. Fully independent of the main window's Run/Hot Reload state; pressing it again replaces the previous window instead of stacking a new one on top.

Agent mode

An AI assistant, opened from the ✦ Assistant title-bar button (or Tools → Assistant, Ctrl+I), with four tools scoped to the sketch you're working on: read the current source, replace it, compile & run it (compile errors and runtime exceptions are reported straight back to the model so it can react), and search this API reference. Needs your own API key for whichever provider you set up in Settings — the IDE doesn't include or pay for one, and the key stays local, never sent anywhere but that provider.

NuGet packages in a sketch

Drop a // nuget: comment at the top of a sketch and pressing Run resolves, downloads (cached under %AppData%\DanaProcessingIde), and links the package before compiling — no .csproj to create or edit.

// nuget: Newtonsoft.Json, 13.0.3

public class MySketch : Sketch
{
    public override void Setup() => Size(600, 400);
}

Sketch → NuGet Packages opens a Browse/Installed package manager (search NuGet.org, pick a version, install/update/remove) that writes these directives for you.

Example gallery

36 built-in sample sketches, open via Sketch → Examples. The window has a search box (accent- and case-insensitive; it also searches inside the code, so PVector or Sphere finds the samples that use them), topic tags with filter chips (2D, 3D, Audio, ML.NET, Particles, Games…), and a code preview; ↑/↓ and Enter work from the keyboard — 2D and 3D, from a minimal Setup()/Draw() to a live-coded beat with the audio engine, an orbiting camera, a GPU-uploaded 3D swarm, parallel prime search, a live-training ML.NET color field, a reinforcement-learning self-play demo, and PDF export. Each one is commented to explain not just what it draws but why it's built that way — a working answer to "how would I actually do X" for most of the API on this page.

Standalone export

File → Export sketch (Ctrl+E) packages the active sketch as a single, self-contained, double-clickable executable via dotnet publish — no .NET runtime install required on the machine that opens it, nothing to explain to whoever you send it to. An optional "modo gadget" (windowless) mode drops the OS title bar entirely — drag it with Alt+click, close it with Alt+F4 — for running the exported sketch as a desktop widget instead of a regular app window.

Crash reporting & feedback

Opt-in, off by default (Settings → Reporte de errores). When enabled, an unhandled exception writes a local crash report; on the next startup, the IDE offers to open a prefilled GitHub issue for it — you review and submit it yourself, nothing is sent automatically or silently. The Help menu also has a standing Report a problem item for general feedback, with no crash required to use it.

Settings

Tools → Settings (Ctrl+,) opens a Settings window, most of it previewed live where that's possible:

SectionControls
ColorsEvery ClayTheme color — applied instantly to the running window.
ShapesCorner radii — take effect on the next app restart.
TypographyFont stacks — take effect on the next app restart.
Rendering2D antialiasing (supersampling, up to 4×) and 3D antialiasing (MSAA, up to 8×) — applied on the next Run, no restart needed.
LanguageSwitches the entire interface between Spanish and English — takes effect on the next app restart.
UpdatesWhether the IDE checks GitHub for a newer release on startup.
Reporte de erroresOpt-in crash reporting (see above) — off by default.
Asistente (Agent mode)Provider, API key, and model for Agent mode (see above).

MainWindow

The full IDE shell: editor and canvas side by side on a wide window, a single "Código"/"Resultado" toggle on a narrow one, a custom title bar, status bar, and error panel. The embedded sketch genuinely stops ticking — not just stops drawing — while the code editor has keyboard focus, or while the canvas itself isn't visible, so an animation- or simulation-heavy sketch doesn't compete with the editor for CPU while you're typing.

ClayTheme

The IDE's visual language — warm off-white surfaces, soft shadows, and an ember-orange accent — driven by the Settings window's Colors section above.

22 · Reference

Keyword index

Every public member referenced in this document, alphabetically. Type to filter. Click any keyword to jump to its documentation.

Back to top
DanaProcessing reference documentation. Generated from the current source tree — GraphicsContext, Sketch, Renderer3D (Silk.NET/OpenGL), PShader, PVector, PImage, PGraphics, PShape, PFont, JSONObject/JSONArray, XML, Table, PerlinNoise, the audio engine (live loops, synths, samples, analysis), the Avalonia host, and the IDE (live diagnostics, code navigation, Hot Reload, Agent mode, NuGet packages, sample gallery, export, crash reporting, settings).