Draw

Immediate-mode 2D drawing, text, textures, cameras, and render effects.

render! is handed a Frame, and everything drawn is drawn through it:

render! = |model, frame| {
    frame.clear!(Color.from_hex_rgb(0x0d1425))
    frame.rectangle!({ x: 40, y: 40, width: 200, height: 90, style: Draw.filled(Color.white) })
    frame.circle!({ center: model.pointer, radius: 18, style: Draw.outlined(Color.red, 3) })
    Ok({})
}

Frame is render-scoped authority. Pass the callback's frame through draw helpers; do not retain it in the model. Nested camera, shader, scissor, render-texture, and blend scopes restore their outer state even when their callback returns Err.

Every effect that takes a Frame is legal only in render!. Resource constructors default_font!, font_from_bytes!, RenderTexture.load! and Shader.from_source! allocate host resources from what the app already has, so they are legal in init!, update!, and tasks. The two that read files out of an asset store, load_store_font! and Shader.from_store!, wait instead: each is legal in init!, where it blocks startup, and in tasks, where it parks the task, and refused in update! and render!. Create long-lived resources in init! and retain them in the model.

Most shapes support equivalent method and free-function forms, such as frame.circle!(cfg) and Draw.circle!(frame, cfg).

Draw.text! draws at an already-resolved top-left origin without a layout pass. Use Text for optional anchor alignment or prepared text.

from_rgba : { r : U8, g : U8, b : U8, a : U8 } -> Rgba

Convert a structural RGBA value at an adapter boundary into roc-ray's color.

alpha_blend : BlendMode

Conventional source-alpha compositing.

additive_blend : BlendMode

Add source and destination colors, useful for light and glow effects.

filled : Rgba -> ShapeStyle

Create a fill-only shape style.

stroke : Rgba, F32 -> Stroke

Create a stroke with color and thickness in logical pixels.

outlined : Rgba, F32 -> ShapeStyle

Create a stroke-only shape style.

default_font! : () => Font

Snapshot raylib's built-in font, metrics included.

Legal in init!, update!, and tasks; refused in render!.

center_in_rect : Rectangle, TextSize -> Vector2

Find the top-left position that centers measured text in a rectangle.

clear! : Frame, Rgba => {}

Clear the active drawing target to a solid color.

Legal in render! only.

rectangle_gradient_v! : Frame, RectangleGradientV => {}

Draw a vertical rectangle gradient.

Legal in render! only.

rectangle_gradient_h! : Frame, RectangleGradientH => {}

Draw a horizontal rectangle gradient.

Legal in render! only.

circle_gradient! : Frame, CircleGradient => {}

Draw a radial circle gradient.

Legal in render! only.

fps! : Frame, Fps => {}

Draw raylib's current frames-per-second counter.

Legal in render! only.

rectangle! : Frame, Rectangle => {}

Draw a filled and/or outlined axis-aligned rectangle.

Legal in render! only.

rounded_rectangle! : Frame, RoundedRectangle => {}

Draw a filled and/or outlined rounded rectangle.

Legal in render! only.

circle! : Frame, Circle => {}

Draw a filled and/or outlined circle.

Legal in render! only.

line! : Frame, Line => {}

Draw a stroked line segment. NoStroke performs no drawing.

Legal in render! only.

triangle! : Frame, Triangle => {}

Draw a filled and/or outlined triangle.

Legal in render! only.

convex_polygon! : Frame, ConvexPolygon => {}

Draw a convex filled polygon and/or an ordered polygon outline. The host triangulates the fill without allocating; fewer than three points do not fill.

Legal in render! only.

triangles! : Frame, TrianglePoints => {}

Draw independent triangles. Every consecutive triple is one triangle. An empty list is a no-op; a non-empty count not divisible by three is a programmer error. Legal in render! only.

triangle_fan! : Frame, TrianglePoints => {}

Draw a triangle fan: the first point is the centre and later points form its perimeter. Fewer than three points are a no-op. Legal in render! only.

triangle_strip! : Frame, TrianglePoints => {}

Draw a triangle strip. Each point after the first two adds one triangle. Fewer than three points are a no-op. Legal in render! only.

line_strip! : Frame, LineStrip => {}

Draw an open polyline. NoStroke and fewer than two points are no-ops. Curve flattening and dash splitting remain pure Roc policy. Legal in render! only.

circle_sector! : Frame, CircleSector => {}

Draw a filled and/or outlined circular sector. Angles are degrees and segments must be at least three. Legal in render! only.

ellipse! : Frame, Ellipse => {}

Draw a filled and/or outlined axis-aligned ellipse. Legal in render! only.

ring! : Frame, Ring => {}

Draw a filled and/or outlined annular sector. Angles are degrees and segments must be at least three. Legal in render! only.

load_store_font! : Store, LoadFont => Try(Font, [PathInvalid, NotFound, ReadFailed, FontLoadFailed, ResourceLimit])

Load a font relative to an explicit asset store.

Legal in init!, where it blocks startup, and in tasks, where it parks the task; refused in update! and render!. The file is read off the frame thread and rasterized when the bytes are back. To load a font from update!, use font_from_bytes! with bytes the app already holds.

font_from_bytes! : FontBytes => Try(Font, [FontLoadFailed, ResourceLimit])

Decode an authored, compile-time embedded font.

The bytes are borrowed while raylib copies and decodes them, so no extra Roc payload-sized buffer is created. Legal in init!, update!, and tasks; refused in render!.

texture_draw : Texture -> TextureDraw

Create a draw configuration covering the whole texture at the origin.

texture_at : Texture, Vec2 -> TextureDraw

Create a draw configuration covering the whole texture at pos.

texture! : Frame, TextureDraw => {}

Draw a texture with explicit source, destination, origin, rotation, and tint.

Legal in render! only.

textured_triangles_3d! : Frame, TexturedTriangles3D => {}

Draw one borrowed indexed batch of perspective-correct textured triangles. The vertex and index list lengths explicitly bound all work in this call; the host consumes both synchronously and retains no geometry. The index count must be a multiple of three and every index must name a vertex. Violating either condition is a programmer error.

Legal in render! only. Normally called inside with_camera_3d!.

texture_instances! : Frame, Texture, List(TextureInstance) => {}

Draw many instances of one texture, in list order, with a single hosted call.

texture! crosses the Roc/host boundary once per sprite, and that crossing is what caps how many sprites a frame can afford. This crosses once for the whole batch and lets the host loop over it, so the cost per instance is the DrawTexturePro call alone. Build the list from application state and pass it straight through; an empty list does not cross at all.

Legal in render! only.

projective_texture! : Frame, ProjectiveTexture => {}

Project a texture onto a validated planar quad with exact homogeneous UV interpolation. This remains one hosted call and preserves active shaders.

Legal in render! only.

with_render_texture! : Frame, RenderTexture, (Frame => Try(result, [ScopeLimit, ScopeUnavailable, ..errors])) => Try(result, [ScopeLimit, ScopeUnavailable, ..errors])

Scope offscreen rendering so BeginTextureMode/EndTextureMode stay paired. Callback errors are returned only after the native target has been restored.

Legal in render! only.

with_shader! : Frame, Shader(uniforms), (Frame => Try(result, [ScopeLimit, ScopeUnavailable, ..errors])) => Try(result, [ScopeLimit, ScopeUnavailable, ..errors])

Scope shader application and upload its complete uniform record before the callback draws. Callback errors are returned only after the previous shader and all of its uniform values have been restored.

Legal in render! only.

with_blend_mode! : Frame, BlendMode, (Frame => Try(result, [ScopeLimit, ..errors])) => Try(result, [ScopeLimit, ..errors])

Scope one of raylib's built-in blend equations. Custom blend factors are deliberately excluded until they can be represented without global state.

Legal in render! only.

with_camera! : Frame, Camera2D, (Frame => Try(result, [ScopeLimit, ..errors])) => Try(result, [ScopeLimit, ..errors])

Draw the callback in world space using this camera.

Legal in render! only.

with_camera_3d! : Frame, Camera3D, (Frame => Try(result, [ScopeLimit, ..errors])) => Try(result, [ScopeLimit, ..errors])

Draw the callback in 3D world space using a perspective camera. Depth testing and depth writes are active until the callback returns.

Legal in render! only.

with_scissor! : Frame, Rect, (Frame => Try(result, [ScopeLimit, ..errors])) => Try(result, [ScopeLimit, ..errors])

Restrict callback drawing to screen-space bounds, and close the scissor however the callback ends, error included.

bounds is in the same logical coordinates as every other drawing call, so it is a rectangle on the surface rather than in framebuffer pixels.

Legal in render! only.

text! : Frame, Text => {}

Draw text using an explicit font, spacing, color, and resolved top-left origin. This performs no measurement or alignment pass.

Legal in render! only.

debug_text! : Frame, DebugText => {}

Draw top-left aligned text with the built-in font and default spacing.

Legal in render! only.

text_at! : Frame, SimpleText => {}

Draw simple top-left aligned text with the built-in font.

Legal in render! only.

Frame

Draw.Frame :: # (opaque)

Opaque, zero-sized authority the host supplies only while it runs render!.

Holding one is what makes a draw call expressible, so drawing cannot happen in init! or update!. Roc does not enforce affine use or encode a frame epoch, so do not retain a Frame in the model: pass the callback's own frame down through helpers.

triangles! : Frame, TrianglePoints => {}

Draw independent triangles. Every consecutive triple is one triangle. An empty list is a no-op; a non-empty count not divisible by three is a programmer error. Legal in render! only.

triangle_fan! : Frame, TrianglePoints => {}

Draw a triangle fan: the first point is the centre and later points form its perimeter. Fewer than three points are a no-op. Legal in render! only.

triangle_strip! : Frame, TrianglePoints => {}

Draw a triangle strip. Each point after the first two adds one triangle. Fewer than three points are a no-op. Legal in render! only.

line_strip! : Frame, LineStrip => {}

Draw an open polyline. NoStroke and fewer than two points are no-ops. Curve flattening and dash splitting remain pure Roc policy. Legal in render! only.

circle_sector! : Frame, CircleSector => {}

Draw a filled and/or outlined circular sector. Angles are degrees and segments must be at least three. Legal in render! only.

ellipse! : Frame, Ellipse => {}

Draw a filled and/or outlined axis-aligned ellipse. Legal in render! only.

ring! : Frame, Ring => {}

Draw a filled and/or outlined annular sector. Angles are degrees and segments must be at least three. Legal in render! only.

clear! : Frame, Rgba => {}

Clear the active drawing target to a solid color.

Legal in render! only.

rectangle_gradient_v! : Frame, RectangleGradientV => {}

Draw a vertical rectangle gradient.

Legal in render! only.

rectangle_gradient_h! : Frame, RectangleGradientH => {}

Draw a horizontal rectangle gradient.

Legal in render! only.

circle_gradient! : Frame, CircleGradient => {}

Draw a radial circle gradient.

Legal in render! only.

fps! : Frame, Fps => {}

Draw raylib's current frames-per-second counter.

Legal in render! only.

rectangle! : Frame, Rectangle => {}

Draw a filled and/or outlined axis-aligned rectangle.

Legal in render! only.

rounded_rectangle! : Frame, RoundedRectangle => {}

Draw a filled and/or outlined rounded rectangle.

Legal in render! only.

circle! : Frame, Circle => {}

Draw a filled and/or outlined circle.

Legal in render! only.

line! : Frame, Line => {}

Draw a stroked line segment. NoStroke performs no drawing.

Legal in render! only.

triangle! : Frame, Triangle => {}

Draw a filled and/or outlined triangle.

Legal in render! only.

convex_polygon! : Frame, ConvexPolygon => {}

Draw a convex filled polygon and/or an ordered polygon outline. The host triangulates the fill without allocating; fewer than three points do not fill.

Legal in render! only.

texture! : Frame, TextureDraw => {}

Draw a texture with explicit source, destination, origin, rotation, and tint.

Legal in render! only.

textured_triangles_3d! : Frame, TexturedTriangles3D => {}

Draw one borrowed indexed batch of perspective-correct textured triangles. The vertex and index list lengths explicitly bound all work in this call; the host consumes both synchronously and retains no geometry. The index count must be a multiple of three and every index must name a vertex. Violating either condition is a programmer error.

Legal in render! only. Normally called inside with_camera_3d!.

texture_instances! : Frame, Texture, List(TextureInstance) => {}

Draw many instances of one texture, in list order, with a single hosted call.

texture! crosses the Roc/host boundary once per sprite, and that crossing is what caps how many sprites a frame can afford. This crosses once for the whole batch and lets the host loop over it, so the cost per instance is the DrawTexturePro call alone. Build the list from application state and pass it straight through; an empty list does not cross at all.

Legal in render! only.

projective_texture! : Frame, ProjectiveTexture => {}

Project a texture onto a validated planar quad with exact homogeneous UV interpolation. This remains one hosted call and preserves active shaders.

Legal in render! only.

with_render_texture! : Frame, RenderTexture, (Frame => Try(result, [ScopeLimit, ScopeUnavailable, ..errors])) => Try(result, [ScopeLimit, ScopeUnavailable, ..errors])

Scope offscreen rendering so BeginTextureMode/EndTextureMode stay paired. Callback errors are returned only after the native target has been restored.

Legal in render! only.

with_shader! : Frame, Shader(uniforms), (Frame => Try(result, [ScopeLimit, ScopeUnavailable, ..errors])) => Try(result, [ScopeLimit, ScopeUnavailable, ..errors])

Scope shader application and upload its complete uniform record before the callback draws. Callback errors are returned only after the previous shader and all of its uniform values have been restored.

Legal in render! only.

with_blend_mode! : Frame, BlendMode, (Frame => Try(result, [ScopeLimit, ..errors])) => Try(result, [ScopeLimit, ..errors])

Scope one of raylib's built-in blend equations. Custom blend factors are deliberately excluded until they can be represented without global state.

Legal in render! only.

with_camera! : Frame, Camera2D, (Frame => Try(result, [ScopeLimit, ..errors])) => Try(result, [ScopeLimit, ..errors])

Draw the callback in world space using this camera.

Legal in render! only.

with_camera_3d! : Frame, Camera3D, (Frame => Try(result, [ScopeLimit, ..errors])) => Try(result, [ScopeLimit, ..errors])

Draw the callback in 3D world space using a perspective camera. Depth testing and depth writes are active until the callback returns.

Legal in render! only.

with_scissor! : Frame, Rect, (Frame => Try(result, [ScopeLimit, ..errors])) => Try(result, [ScopeLimit, ..errors])

Restrict callback drawing to screen-space bounds, and close the scissor however the callback ends, error included.

bounds is in the same logical coordinates as every other drawing call, so it is a rectangle on the surface rather than in framebuffer pixels.

Legal in render! only.

text! : Frame, Text => {}

Draw text using an explicit font, spacing, color, and resolved top-left origin. This performs no measurement or alignment pass.

Legal in render! only.

debug_text! : Frame, DebugText => {}

Draw top-left aligned text with the built-in font and default spacing.

Legal in render! only.

text_at! : Frame, SimpleText => {}

Draw simple top-left aligned text with the built-in font.

Legal in render! only.

size! : Frame => FrameSize

How big the surface being drawn to right now is.

Normally this is the window's logical drawing size -- the same value Window.Snapshot.size reports, in the same coordinate space as mouse input and every drawing call. Inside with_render_texture! it is the render target's size instead, because that is what the callback's coordinates are relative to.

This is the size of the drawing surface, so it is F32 where Window.Snapshot.size is I32: it feeds rectangles, text anchors and centre points directly, and a render target's dimensions are already F32 on Texture. Window.Snapshot.size stays I32 because it is also the thing Window.suggest_size! asks for.

Reach for this when laying something out against the surface -- a HUD in a corner, a title centred across the top. Layout decisions that update! also has to make, such as which arrangement to use or what the pointer is over, belong on input.window where the rest of application logic can see them.

Legal in render! only.

FrameSize : {
    width : F32,
    height : F32,
}

Dimensions of the surface render! is currently drawing to.

Texture : Texture

Host-owned GPU texture, the same type Assets loads and generates.

Named here as well so drawing code can keep a texture in its model without importing Assets; Draw.Texture, Assets.Texture and the platform's Texture are one type, not three.

Vector2 : Vec2

Two-dimensional vector used by drawing records.

Rect : Rect

Axis-aligned rectangle used by drawing records.

Camera2D : Camera2D

Pure 2D camera settings.

Camera3D : Camera3D

Pure perspective camera settings.

Vector3 : Vec3

Three-dimensional vector used by 3D drawing records.

TexturedVertex3D : { position : Vector3, uv : Vector2, tint : Rgba }

One vertex in an immediate textured triangle batch.

TexturedTriangles3D : { texture : Texture, vertices : List(TexturedVertex3D), indices : List(U32) }

A borrowed indexed triangle batch. Every consecutive group of three indices is one triangle. Indices must be in range and their count a multiple of three.

Fill : [NoFill, Fill(Rgba)]

Optional shape fill.

Stroke : [NoStroke, Stroke({ color : Rgba, thickness : F32 })]

Optional shape outline with color and thickness.

ShapeStyle : {
    fill : Fill,
    stroke : Stroke,
}

Combined fill and outline applied by shape helpers.

Rectangle : {
    x : F32,
    y : F32,
    width : F32,
    height : F32,
    style : ShapeStyle,
}

Axis-aligned rectangle and its style.

RoundedRectangle : {
    x : F32,
    y : F32,
    width : F32,
    height : F32,
    radius : F32,
    segments : I32,
    style : ShapeStyle,
}

Rounded rectangle; radius and segment count control corner tessellation.

RectangleGradientV : {
    x : F32,
    y : F32,
    width : F32,
    height : F32,
    color_top : Rgba,
    color_bottom : Rgba,
}

Vertical rectangle gradient from top to bottom.

RectangleGradientH : {
    x : F32,
    y : F32,
    width : F32,
    height : F32,
    color_left : Rgba,
    color_right : Rgba,
}

Horizontal rectangle gradient from left to right.

Circle : {
    center : Vector2,
    radius : F32,
    style : ShapeStyle,
}

Circle and its style.

CircleGradient : {
    center : Vector2,
    radius : F32,
    color_inner : Rgba,
    color_outer : Rgba,
}

Radial gradient from inner to outer color.

Line : {
    start : Vector2,
    end : Vector2,
    stroke : Stroke,
}

Line segment and stroke.

Triangle : {
    a : Vector2,
    b : Vector2,
    c : Vector2,
    style : ShapeStyle,
}

Triangle vertices and style.

ConvexPolygon : {
    points : List(Vector2),
    style : ShapeStyle,
}

A simple convex polygon. Points must be ordered around the boundary (clockwise or counter-clockwise). Filled concave or self-intersecting polygons are not supported; outlines accept any ordered point path.

TrianglePoints : {
    points : List(Vector2),
    color : Rgba,
}

Positions already assembled into a triangle topology, all drawn in one color. points is not a path: contour, winding, and hole handling belong to the pure Roc tessellator that produced this list.

LineStrip : {
    points : List(Vector2),
    stroke : Stroke,
}

An ordered, open polyline. Each adjacent pair is drawn with stroke.

CircleSector : {
    center : Vector2,
    radius : F32,
    start_angle : F32,
    end_angle : F32,
    segments : I32,
    style : ShapeStyle,
}

A circular sector from start_angle to end_angle, measured in degrees.

Ellipse : {
    center : Vector2,
    radius_x : F32,
    radius_y : F32,
    style : ShapeStyle,
}

An axis-aligned ellipse with independent horizontal and vertical radii.

Ring : {
    center : Vector2,
    inner_radius : F32,
    outer_radius : F32,
    start_angle : F32,
    end_angle : F32,
    segments : I32,
    style : ShapeStyle,
}

An annular sector from start_angle to end_angle, measured in degrees.

Fps : {
    pos : Vector2,
    size : F32,
    color : Rgba,
}

Position, size, and color for the FPS counter.

GlyphMetrics : {
    codepoint : U32,
    advance_x : F32,
    offset_x : F32,
    offset_y : F32,
    width : F32,
    height : F32,
}

Scalar metrics for one glyph, shared with platform-independent packages.

Text : {
    pos : Vector2,
    text : Str,
    size : F32,
    spacing : F32,
    color : Rgba,
    font : Font,
}

Fully configured text draw at a resolved top-left origin.

DebugText : {
    pos : Vector2,
    text : Str,
    size : F32,
    color : Rgba,
}

Built-in-font text intended for quick diagnostics.

SimpleText : {
    pos : Vector2,
    text : Str,
    size : F32,
    color : Rgba,
}

Built-in-font text with default spacing.

LoadFont : {
    path : Str,
    size : I32,
}

Font path and base pixel size.

TextureDraw : TextureDrawConfig

Resolved texture draw configuration: which texture, which part of it, where it goes, and how it is rotated and tinted.

TextureDrawConfig in the signature is the module-private record this aliases; Draw.TextureDraw is the name to write. Build one with texture_draw, texture_at, or the TextureDrawBuilder combinators.

TextureInstance : TextureInstanceConfig

One instance of a batched texture draw. These are the fields of TextureDraw minus the texture, which the batch supplies once.

TextureInstanceConfig in the signature is the module-private record this aliases; Draw.TextureInstance is the name to write.

ProjectiveQuadCorners : {
    top_left : Vec2,
    bottom_left : Vec2,
    bottom_right : Vec2,
    top_right : Vec2,
}

Four ordered corners of a projected planar surface.

ProjectiveQuad

Draw.ProjectiveQuad :: # (opaque)

A finite, convex planar projection with a bounded homography. Construct it with ProjectiveQuad.from_corners; the opaque representation holds the homogeneous weights needed for exact perspective-correct interpolation.

from_corners : ProjectiveQuadCorners -> Try(ProjectiveQuad, [NonFiniteQuad, DegenerateQuad, NonConvexQuad, ProjectiveHorizon])

Validate four boundary-ordered corners and solve their projective weights. A single homography cannot represent a concave, self-intersecting, or horizon-crossing destination, so those states are rejected here.

project : ProjectiveQuad, Vec2 -> Vec2

Project a unit-square coordinate onto the destination surface. This uses the same homography as rendering and is useful for aligned overlays.

ProjectiveTexture : {
    texture : Texture,
    source : Rect,
    quad : ProjectiveQuad,
    tint : Rgba,
}

Texture and source region projected exactly onto a validated planar quad.

RenderTexture

Draw.RenderTexture :: # (opaque)

Host-owned framebuffer. Its texture-shaped box has a distinct host kind; the host rejects ordinary textures before entering an offscreen scope. Releasing the final reference unloads the framebuffer and both attachments.

load! : RenderTextureSize => Try(RenderTexture, [RenderTextureLoadFailed, ResourceLimit])

Allocate an offscreen framebuffer.

Legal in init!, update!, and tasks; refused in render!.

texture : RenderTexture -> Texture

Read-only view of this render target's color attachment.

source : RenderTexture -> Rect

Vertically inverted full-source rectangle for drawing the color attachment.

stub : RenderTexture

Resource-free render target for pure tests.

The handle never resolves to a host resource, so entering a scope with it is refused the way a released target is. Its color attachment is Texture.stub with zero dimensions; copy it with the dimensions the test needs. Do not use it to test drawing, offscreen scopes, or resource lifetime.

FontFormat

:= [Ttf, Otf]

Which font file format FontBytes holds. The bytes are decoded by format rather than by sniffing them, so a mislabelled file fails to load instead of loading as something else.

FontBytes : { format : FontFormat, bytes : List(U8), size : I32 }

An authored font embedded with a compile-time file import, plus the pixel size to rasterize its glyph atlas at. size is baked into the atlas, so drawing at a much larger size scales that atlas up rather than re-rasterizing; load the font again at the size you need instead.

BlendMode : [
    Alpha,
    Additive,
    Multiplied,
    AddColors,
    SubtractColors,
    AlphaPremultiply,
]

Built-in blend equations with scoped application through with_blend_mode!.

ScopeError : [ScopeLimit, ScopeUnavailable]

A scoped renderer could not be opened because its bounded native stack is full or a transferred host resource no longer resolves.