Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / UI

textures

Draw.Image(wall, dx, dy, dw, dh) · Draw.Image(wall, sx, sy, sw, sh, dx, dy, dw, dh) — a bitmap your app ships, named by a compile-checked identifier

Drop `.png`, `.jpg` or `.webp` files into `model/textures/` and blit them onto a canvas with `Draw.Image(wall, …)`. The name is checked at compile time, so a typo is an error rather than a blank sprite. Textures are the raster half of your app's art — the vector half is `Svg(name)`, and a single-colour glyph is `Icon(name)`.

preview2 examples compiled by CIuicanvasassetsgraphics

Summary#

A texture is a file in your app, not data. Put a .png in model/textures/ and it becomes part of your app's vocabulary:

model/
  textures/
    wall.png
    floor.png
    sprites.png

wall.png is now drawable as Draw.Image(wall, …). There is nothing to register and nothing to import.

Textures are the third kind of art an app can ship, and they divide by what the picture is rather than by taste:

you haveput it indraw it with
a single-colour glyph that should follow your textmodel/icons/Icon(Icons.Search)
a vector illustration, logo or backgroundmodel/art/Svg(Art.Hexgrid)
a bitmap — a wall texture, a sprite sheet, a photographmodel/textures/Draw.Image(wall, …)

Signature#

Draw.Image(wall, dx, dy, dw, dh)                          // the whole texture, into a destination rectangle
Draw.Image(wall, sx, sy, sw, sh, dx, dy, dw, dh)          // a SOURCE rectangle of it, into a destination one
Draw.Image(url, …)                                        // the same two forms, over a runtime url

Description#

The name is checked#

Draw.Image(wall, …) names the texture by a bare identifier, checked against the textures your app actually ships. A typo is a compile error that lists what there is:

`Draw.Image` takes a declared texture or a url — 'walll' is neither a variable in scope nor a
declared texture (declared: floor, wall). Did you mean 'wall'?

Because the name is an identifier, a texture's file name must be one toobrick_wall.png, not brick-wall.png. A kebab-case file is rejected with the rename to make.

A url still works, and a collision is refused#

Unlike Icon and Svg, this first argument can legitimately be an expression: a url is genuinely a runtime value sometimes — a user's uploaded avatar, a signed download link.

[Page("/avatar")]
[AllowAnonymous]
component Avatar() {
  string src = "/uploads/me.png";

  on frame (double dt) {
    Draw.Clear("#111");
    Draw.Image(src, 8, 8, 64, 64);       // an expression — the url form
  }

  render { Canvas(w: 80, h: 80); }
}

The texture form is the same call with a declared name in that first position — and both argument forms in one page, which is the whole surface:

[Page("/wall")]
[AllowAnonymous]
component Wall() {
  on frame (double dt) {
    Draw.Clear("#1a1a22");
    Draw.Image(Textures.Brick1, 0, 0, 32, 32);                 // the WHOLE texture, into a destination rectangle
    Draw.Image(Textures.Brick1, 0, 0, 4, 4, 40, 0, 64, 64);    // a SOURCE rectangle of it, into a destination one
  }

  render { Canvas(w: 112, h: 64); }
}

That example is compiled on every docs build against a real image filecanvas-texture is a complete sample app shipping textures/wall.png, and osy docs sample canvas-texture hands you the whole thing.

So a bare identifier could mean either, and when it means both the compiler refuses rather than picking:

`Draw.Image(wall, …)` is ambiguous — 'wall' is both a declared texture and a variable in scope,
and the two draw different things. Rename one of them.

Every precedence rule here would produce a silent bug in one direction or the other — a texture drawn where a variable was meant, or a local added months later quietly changing what a call site draws. Renaming one of the two costs seconds; finding either of those costs an afternoon.

Which formats, and why the file name does not decide#

.png, .jpg/.jpeg and .webp — the three raster formats a browser decodes into a canvas.

The type a texture is served under is read from its bytes, never from its extension. A consequence worth knowing: a file whose name disagrees with its contents is a compile error rather than a quiet re-label.

'wall.png' is really a JPEG, whatever its extension says — rename it to 'wall.jpg'.

An .svg in model/textures/ is not a texture. A vector image is markup that has to be sanitized before it reaches a page, which is what model/art/ and Svg(name) are for.

Sampling a source rectangle#

The nine-argument form takes a rectangle of the texture and scales it into a rectangle of the canvas. It is what a sprite sheet needs, and what a textured raycaster needs — a one-pixel-wide column of the texture stretched to a wall's height:

// one screen column: texel column `texX` of a 64x64 texture, over the wall's full height
Draw.Image(wall, texX, 0, 1, 64, x, top, colWidth, wallHeight);

Shading a texture#

A blit paints the texture's own pixels, so lighting is a second pass over the top rather than a colour argument — draw the texture, then wash it with a translucent rectangle:

Draw.Image(wall, texX, 0, 1, 64, x, top, colWidth, wallHeight);
Draw.Rect(x, top, colWidth, wallHeight, "rgba(0,0,0,0.35)");   // distance falloff

One texture per name#

Two files claiming the same stem is an error naming both, because a name has to resolve to one file. The same rule applies when a kit vendors its textures into your tree.

Where texture files live, and how to move them#

The default is any textures/ folder in your source tree. Declare the role in app.osy to put them somewhere else:

app Arcade {
  model    "model/**/*.osy";
  textures "assets/textures/*.png";
}

Reading a texture's pixels#

Texture.Pixels(wall) answers the texture's pixels as a List<int> — one packed 0xRRGGBB colour per pixel, in row order — so a software renderer can sample it. Texture.Width(wall) and Texture.Height(wall) give its size.

It answers the whole buffer, once, rather than a texel at a time, and that is a performance contract rather than a convenience: a call costs roughly seven times an arithmetic operation, so asking per texel would cap a per-pixel effect at a few thousand pixels a frame before any of its own work. Read it into a field, index it in the loop.

⚠ A texture is decoded by the browser, so Texture.Pixels answers an empty list until it has been — the same "one frame away" rule Draw.Image follows. Read it in the frame body until it arrives, not in on mount, which runs once and would lose the race permanently.

List<int> texels = new List<int>();

on frame (double dt) {
  if (texels.Count == 0) { texels = Texture.Pixels(wall); }
  // …now index it: texels[Texture.Width(wall) * y + x]
}

Colours are 24-bit RGB, not 32-bit ARGB, because an Osy# int is a signed 32-bit integer — any alpha above 0x7F would overflow it and hand you negative colours. A pixel buffer is opaque; transparency is expressed by not drawing.

Size, and the limits#

A texture's intrinsic size is read at compile time, from the image header — so it is known before anything decodes, and a header claiming an absurd size is refused there rather than becoming a very large canvas later. A texture may be at most 8192×8192 and 8 MB.

Passing a texture around — Textures is a type#

Textures is a type, so a texture is a value you can pass and store like any other. A helper that blits one takes it as a parameter:

void Blit(Textures tex, int x) {
  Draw.Image(tex, x, 0, 128, 128);
}

// at the call site
Blit(Textures.Wall1, 0);

The same holds for a return type, a local, and a field on an entity. Because a stored texture is keyed by the file's name rather than by a position in a list, adding a new file never changes what an already-stored row means.

See also#

  • Canvas — the surface Draw.Image paints on, Draw.Pixels for a whole pixel buffer, and the rest of the Draw.* vocabulary.
  • icons — the single-colour glyph vocabulary, and the closest analogue to how a texture is named.
  • component — where an on frame body lives.

Related

Canvas

A drawing surface, and the verbs that paint on it. Put a `Canvas` in a render block, call `Draw.*` from an `on frame`…

icons

Drop `.svg` files into `model/icons/` and render them with `Icon(Icons.Search)`. The name is checked at compile time…

component

The one archetype for all UI: a bounded reactive unit — typed props, reactive members (fields, `live`…