---
title: "text"
description: "The text namespace — the engine's Luau API reference for text."
section: "API Reference"
slug: "api-text"
canonical: "https://origozero.ai/docs/api-text"
updated: "2026-09-05T23:13:47.331171628+00:00"
tags: ["api", "reference"]
---

# text

The `text` namespace — 49 functions.

## globals/text/alive {#globals-text-alive}

```lua
text.alive(handle: any?) -> boolean
```

Whether the text system still holds this handle — true between
`text.create` and the `text.destroy` that released it.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

**Returns** `boolean` — True while the handle is live.

```lua
if not text.alive(h) then h = text.create({ content = "again" }) end
```

## globals/text/count {#globals-text-count}

```lua
text.count() -> number
```

How many text objects the text system is holding — the number that
moves when `text.create` and `text.destroy` are called.

**Returns** `number` — The live text-object count.

```lua
local before = text.count()
```

## globals/text/create {#globals-text-create}

```lua
text.create(options: table) -> any
```

Create a text handle from an initial content + style table. The handle
owns a runtime GPU texture (see `text.textureGuid`); pass it to every other
call.

**Parameters**

- `options` `table` — Table of `content` plus style fields (fontSize, color,
alignment, richText, maxWidth, ...).

**Returns** `any` — An opaque text handle, or nil if the text system is unavailable.

```lua
local h = text.create({ content = "Hello", fontSize = 48 })
```

## globals/text/destroy {#globals-text-destroy}

```lua
text.destroy(handle: any?) -> boolean
```

Destroy a text handle and release its raster + glyph layout.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

**Returns** `boolean` — True when the text system held the handle and released it; false for a handle it did not have.

```lua
text.destroy(h)
```

## globals/text/face {#globals-text-face}

```lua
text.face(handle: any?) -> any
```

Which font face one handle actually shaped with, and whether that is
the family its style asked for. `requested` is what was asked, `resolved`
is the face that answered, `matched` says whether they agree and `reason`
says why when they do not — one of `text.faceReasons()`. A style that named
no family reports `noFamilyRequested`: it got the default because it asked
for nothing, so `reason` rather than `matched` is what an alert switches
on. `faces` lists
every face the shaper used, most glyphs first, so a fallback that covered
part of the string is visible alongside the face that covered the rest.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

**Returns** `any` — `{ requested, resolved, postScriptName, matched, reason, faces, glyphCount }`, or nil for a handle the text system does not hold.

```lua
local r = text.face(h).reason; if r == "familyUnknown" or r == "familyNotSelectable" then print(r) end
```

## globals/text/faceReasons {#globals-text-facereasons}

```lua
text.faceReasons() -> { string }
```

Every reason the face readings give for a label or a family not being
in the family a style named, nearest cause first. `text.face` gives them
for one label; `font.reconcile()` also gives `familyCoveredNoGlyph`, which
it can only reach by laying the family out under its own weights and over
several scripts.

**Returns** `{ string }` — Array of reason strings.

```lua
for _, r in ipairs(text.faceReasons()) do print(r) end
```

## globals/text/listFonts {#globals-text-listfonts}

```lua
text.listFonts() -> { string }
```

List the font families currently available to the text system.

**Returns** `{ string }` — Array of font-family name strings.

```lua
local fonts = text.listFonts()
```

## globals/text/loadFont {#globals-text-loadfont}

```lua
text.loadFont(ref: any?) -> any
```

Load a font from an asset reference so it becomes available to
`setStyle`'s `fontFamily`.

**Parameters**

- `ref` `any` _(optional)_ — Font asset reference or path.

**Returns** `any` — The loaded font-family name, or nil on failure.

```lua
text.loadFont(asset.ref("fonts.inter", "font"))
```

## globals/text/measure {#globals-text-measure}

```lua
text.measure(handle: any?) -> any
```

Measure the rasterised text in pixels without producing a texture.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

**Returns** `any` — Table with `width` and `height` in pixels.

```lua
local size = text.measure(h)
```

## globals/text/observe {#globals-text-observe}

```lua
text.observe() -> any
```

Everything the text system is holding right now. `count` is the live
text objects; `objects` is one row each, carrying its content, the style
it was laid out with, its measured extent, whether it is dirty, the `face`
the shaper actually used, the `owner` entity whose component created it
with whether that entity is still there, and the `raster` texture its last
rasterisation landed in with the bytes it costs. `orphans` is the subset
whose owning entity is gone, `fonts` the families the shaper can resolve,
and `raster` the glyph-raster bytes with the pool they belong to named.
Built when you ask, so it costs nothing per frame and reads the same in
edit mode as in play.

**Returns** `any` — `{ count, objects, orphans, fonts, dirty, raster }`.

```lua
local live = text.observe().count
```

## globals/text/orphans {#globals-text-orphans}

```lua
text.orphans() -> { any }
```

The text objects whose owning entity no longer exists — a quad the
engine is still holding for something that has been despawned. Each row is
the same shape `text.observe().objects` carries.

**Returns** `{ any }` — Array of text-object rows with a dead owner.

```lua
print(#text.orphans() .. " labels outlived their entity")
```

## globals/text/rasterMemory {#globals-text-rastermemory}

```lua
text.rasterMemory() -> any
```

The glyph-raster bytes, broken out of the runtime GPU texture pool.
`bytes` is summed off the same map `renderer.gpuMemory().textures` is
totalled from, so `shareOfPool` is a share of that number rather than a
second count of the same memory.

**Returns** `any` — `{ pool, bytes, textures, poolBytes, shareOfPool }`.

```lua
local r = text.rasterMemory(); print(r.bytes .. " of " .. r.poolBytes)
```

## globals/text/rasterize {#globals-text-rasterize}

```lua
text.rasterize(handle: any?, texture: any?, scale: number?) -> any
```

Rasterise the handle's current text + style into the given runtime GPU
texture. Bind that texture's guid as a material's `base_color_texture` to
display the text; re-rasterising the same texture overwrites it in place.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.
- `texture` `any` _(optional)_ — Destination GPU texture handle (`renderer.texture.create`) or its
guid string — WHERE the raster lands.
- `scale` `number` _(optional)_ — World/pixel scale factor for the raster (default 1.0).

**Returns** `any` — Table with `width` and `height` (in pixels), or nil if nothing rasterised.

```lua
local tex = renderer.texture.create({ width = 256, height = 64 })
local r = text.rasterize(h, tex, 1.0)
```

## globals/text/setStyle {#globals-text-setstyle}

```lua
text.setStyle(handle: any?, style: table) -> boolean
```

Replace the handle's style. Fields not present keep their current
value.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.
- `style` `table` — Style table (fontSize, color, alignment, outline, ...).

**Returns** `boolean` — True when the text system held the handle and took the style; false when it did not.

```lua
text.setStyle(h, { fontSize = 64, color = "yellow" })
```

## globals/text/setText {#globals-text-settext}

```lua
text.setText(handle: any?, content: string) -> boolean
```

Replace the handle's text content.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.
- `content` `string` — New text string.

**Returns** `boolean` — True when the text system held the handle and took the content; false when it did not, which is how a caller learns its handle went away.

```lua
if not text.setText(h, "HP: 100") then h = text.create({ content = "HP: 100" }) end
```

## globals/text/textureGuid {#globals-text-textureguid}

```lua
text.textureGuid(handle: any?) -> string?
```

The runtime GPU texture guid this handle rasterises into — bind it as a
material texture (`base_color_texture`) to display the text.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

**Returns** `string?` — The texture guid string, or nil for a handle the text system does not hold.

```lua
entity(id).component.get("Material"):setTexture("base_color_texture", text.textureGuid(h))
```

## modules/text/README {#modules-text-readme}

```lua
require("@builtin/modules/api/engine/text") -- text (also available as global 'text')
```

Text rasterisation resource — create a text handle, set its content and style, then rasterise it to a texture for display, and observe what the text system is holding. Public Luau surface over the `__text` and `__textObserve` Internal FFI namespaces.

Usage: local text = require("@builtin/modules/api/engine/text")
Also available as global: text

## modules/text/alive {#modules-text-alive}

```lua
alive(handle: any): boolean
```

Whether the text system still holds this handle — true between
`text.create` and the `text.destroy` that released it.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

```lua
if not text.alive(h) then h = text.create({ content = "again" }) end
```

## modules/text/count {#modules-text-count}

```lua
count(): number
```

How many text objects the text system is holding — the number that
moves when `text.create` and `text.destroy` are called.

```lua
local before = text.count()
```

## modules/text/create {#modules-text-create}

```lua
create(options: table): any
```

Create a text handle from an initial content + style table. The handle
owns a runtime GPU texture (see `text.textureGuid`); pass it to every other
call.

**Parameters**

- `options` `table` — Table of `content` plus style fields (fontSize, color,
alignment, richText, maxWidth, ...).

```lua
local h = text.create({ content = "Hello", fontSize = 48 })
```

## modules/text/destroy {#modules-text-destroy}

```lua
destroy(handle: any): boolean
```

Destroy a text handle and release its raster + glyph layout.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

```lua
text.destroy(h)
```

## modules/text/face {#modules-text-face}

```lua
face(handle: any): any
```

Which font face one handle actually shaped with, and whether that is
the family its style asked for. `requested` is what was asked, `resolved`
is the face that answered, `matched` says whether they agree and `reason`
says why when they do not — one of `text.faceReasons()`. A style that named
no family reports `noFamilyRequested`: it got the default because it asked
for nothing, so `reason` rather than `matched` is what an alert switches
on. `faces` lists
every face the shaper used, most glyphs first, so a fallback that covered
part of the string is visible alongside the face that covered the rest.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

```lua
local r = text.face(h).reason; if r == "familyUnknown" or r == "familyNotSelectable" then print(r) end
```

## modules/text/faceReasons {#modules-text-facereasons}

```lua
faceReasons(): { string }
```

Every reason the face readings give for a label or a family not being
in the family a style named, nearest cause first. `text.face` gives them
for one label; `font.reconcile()` also gives `familyCoveredNoGlyph`, which
it can only reach by laying the family out under its own weights and over
several scripts.

```lua
for _, r in ipairs(text.faceReasons()) do print(r) end
```

## modules/text/listFonts {#modules-text-listfonts}

```lua
listFonts(): { string }
```

List the font families currently available to the text system.

```lua
local fonts = text.listFonts()
```

## modules/text/loadFont {#modules-text-loadfont}

```lua
loadFont(ref: any): any
```

Load a font from an asset reference so it becomes available to
`setStyle`'s `fontFamily`.

**Parameters**

- `ref` `any` _(optional)_ — Font asset reference or path.

```lua
text.loadFont(asset.ref("fonts.inter", "font"))
```

## modules/text/measure {#modules-text-measure}

```lua
measure(handle: any): any
```

Measure the rasterised text in pixels without producing a texture.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

```lua
local size = text.measure(h)
```

## modules/text/observe {#modules-text-observe}

```lua
observe(): any
```

Everything the text system is holding right now. `count` is the live
text objects; `objects` is one row each, carrying its content, the style
it was laid out with, its measured extent, whether it is dirty, the `face`
the shaper actually used, the `owner` entity whose component created it
with whether that entity is still there, and the `raster` texture its last
rasterisation landed in with the bytes it costs. `orphans` is the subset
whose owning entity is gone, `fonts` the families the shaper can resolve,
and `raster` the glyph-raster bytes with the pool they belong to named.
Built when you ask, so it costs nothing per frame and reads the same in
edit mode as in play.

```lua
local live = text.observe().count
```

## modules/text/orphans {#modules-text-orphans}

```lua
orphans(): { any }
```

The text objects whose owning entity no longer exists — a quad the
engine is still holding for something that has been despawned. Each row is
the same shape `text.observe().objects` carries.

```lua
print(#text.orphans() .. " labels outlived their entity")
```

## modules/text/rasterMemory {#modules-text-rastermemory}

```lua
rasterMemory(): any
```

The glyph-raster bytes, broken out of the runtime GPU texture pool.
`bytes` is summed off the same map `renderer.gpuMemory().textures` is
totalled from, so `shareOfPool` is a share of that number rather than a
second count of the same memory.

```lua
local r = text.rasterMemory(); print(r.bytes .. " of " .. r.poolBytes)
```

## modules/text/rasterize {#modules-text-rasterize}

```lua
rasterize(handle: any, texture: any, scale: number?): any
```

Rasterise the handle's current text + style into the given runtime GPU
texture. Bind that texture's guid as a material's `base_color_texture` to
display the text; re-rasterising the same texture overwrites it in place.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.
- `texture` `any` _(optional)_ — Destination GPU texture handle (`renderer.texture.create`) or its
guid string — WHERE the raster lands.
- `scale` `number?` _(optional)_ — World/pixel scale factor for the raster (default 1.0).

```lua
local tex = renderer.texture.create({ width = 256, height = 64 })
local r = text.rasterize(h, tex, 1.0)
```

## modules/text/setStyle {#modules-text-setstyle}

```lua
setStyle(handle: any, style: table): boolean
```

Replace the handle's style. Fields not present keep their current
value.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.
- `style` `table` — Style table (fontSize, color, alignment, outline, ...).

```lua
text.setStyle(h, { fontSize = 64, color = "yellow" })
```

## modules/text/setText {#modules-text-settext}

```lua
setText(handle: any, content: string): boolean
```

Replace the handle's text content.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.
- `content` `string` — New text string.

```lua
if not text.setText(h, "HP: 100") then h = text.create({ content = "HP: 100" }) end
```

## modules/text/textureGuid {#modules-text-textureguid}

```lua
textureGuid(handle: any): string?
```

The runtime GPU texture guid this handle rasterises into — bind it as a
material texture (`base_color_texture`) to display the text.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

```lua
entity(id).component.get("Material"):setTexture("base_color_texture", text.textureGuid(h))
```

## typed/builtin//modules/api/engine/text/text/alive {#typed-builtin-modules-api-engine-text-text-alive}

```lua
text.alive(handle: any?) -> boolean
```

Whether the text system still holds this handle — true between
`text.create` and the `text.destroy` that released it.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

**Returns** `boolean` — True while the handle is live.

```lua
if not text.alive(h) then h = text.create({ content = "again" }) end
```

## typed/builtin//modules/api/engine/text/text/count {#typed-builtin-modules-api-engine-text-text-count}

```lua
text.count() -> number
```

How many text objects the text system is holding — the number that
moves when `text.create` and `text.destroy` are called.

**Returns** `number` — The live text-object count.

```lua
local before = text.count()
```

## typed/builtin//modules/api/engine/text/text/create {#typed-builtin-modules-api-engine-text-text-create}

```lua
text.create(options: table) -> any
```

Create a text handle from an initial content + style table. The handle
owns a runtime GPU texture (see `text.textureGuid`); pass it to every other
call.

## typed/builtin//modules/api/engine/text/text/destroy {#typed-builtin-modules-api-engine-text-text-destroy}

```lua
text.destroy(handle: any?) -> boolean
```

Destroy a text handle and release its raster + glyph layout.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

**Returns** `boolean` — True when the text system held the handle and released it; false for a handle it did not have.

```lua
text.destroy(h)
```

## typed/builtin//modules/api/engine/text/text/face {#typed-builtin-modules-api-engine-text-text-face}

```lua
text.face(handle: any?) -> any
```

Which font face one handle actually shaped with, and whether that is
the family its style asked for. `requested` is what was asked, `resolved`
is the face that answered, `matched` says whether they agree and `reason`
says why when they do not — one of `text.faceReasons()`. A style that named
no family reports `noFamilyRequested`: it got the default because it asked
for nothing, so `reason` rather than `matched` is what an alert switches
on. `faces` lists
every face the shaper used, most glyphs first, so a fallback that covered
part of the string is visible alongside the face that covered the rest.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

**Returns** `any` — `{ requested, resolved, postScriptName, matched, reason, faces, glyphCount }`, or nil for a handle the text system does not hold.

```lua
local r = text.face(h).reason; if r == "familyUnknown" or r == "familyNotSelectable" then print(r) end
```

## typed/builtin//modules/api/engine/text/text/faceReasons {#typed-builtin-modules-api-engine-text-text-facereasons}

```lua
text.faceReasons() -> { string }
```

Every reason the face readings give for a label or a family not being
in the family a style named, nearest cause first. `text.face` gives them
for one label; `font.reconcile()` also gives `familyCoveredNoGlyph`, which
it can only reach by laying the family out under its own weights and over
several scripts.

**Returns** `{ string }` — Array of reason strings.

```lua
for _, r in ipairs(text.faceReasons()) do print(r) end
```

## typed/builtin//modules/api/engine/text/text/listFonts {#typed-builtin-modules-api-engine-text-text-listfonts}

```lua
text.listFonts() -> { string }
```

List the font families currently available to the text system.

**Returns** `{ string }` — Array of font-family name strings.

```lua
local fonts = text.listFonts()
```

## typed/builtin//modules/api/engine/text/text/loadFont {#typed-builtin-modules-api-engine-text-text-loadfont}

```lua
text.loadFont(ref: any?) -> any
```

Load a font from an asset reference so it becomes available to
`setStyle`'s `fontFamily`.

**Parameters**

- `ref` `any` _(optional)_ — Font asset reference or path.

**Returns** `any` — The loaded font-family name, or nil on failure.

```lua
text.loadFont(asset.ref("fonts.inter", "font"))
```

## typed/builtin//modules/api/engine/text/text/measure {#typed-builtin-modules-api-engine-text-text-measure}

```lua
text.measure(handle: any?) -> any
```

Measure the rasterised text in pixels without producing a texture.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

**Returns** `any` — Table with `width` and `height` in pixels.

```lua
local size = text.measure(h)
```

## typed/builtin//modules/api/engine/text/text/observe {#typed-builtin-modules-api-engine-text-text-observe}

```lua
text.observe() -> any
```

Everything the text system is holding right now. `count` is the live
text objects; `objects` is one row each, carrying its content, the style
it was laid out with, its measured extent, whether it is dirty, the `face`
the shaper actually used, the `owner` entity whose component created it
with whether that entity is still there, and the `raster` texture its last
rasterisation landed in with the bytes it costs. `orphans` is the subset
whose owning entity is gone, `fonts` the families the shaper can resolve,
and `raster` the glyph-raster bytes with the pool they belong to named.
Built when you ask, so it costs nothing per frame and reads the same in
edit mode as in play.

**Returns** `any` — `{ count, objects, orphans, fonts, dirty, raster }`.

```lua
local live = text.observe().count
```

## typed/builtin//modules/api/engine/text/text/orphans {#typed-builtin-modules-api-engine-text-text-orphans}

```lua
text.orphans() -> { any }
```

The text objects whose owning entity no longer exists — a quad the
engine is still holding for something that has been despawned. Each row is
the same shape `text.observe().objects` carries.

**Returns** `{ any }` — Array of text-object rows with a dead owner.

```lua
print(#text.orphans() .. " labels outlived their entity")
```

## typed/builtin//modules/api/engine/text/text/rasterMemory {#typed-builtin-modules-api-engine-text-text-rastermemory}

```lua
text.rasterMemory() -> any
```

The glyph-raster bytes, broken out of the runtime GPU texture pool.
`bytes` is summed off the same map `renderer.gpuMemory().textures` is
totalled from, so `shareOfPool` is a share of that number rather than a
second count of the same memory.

**Returns** `any` — `{ pool, bytes, textures, poolBytes, shareOfPool }`.

```lua
local r = text.rasterMemory(); print(r.bytes .. " of " .. r.poolBytes)
```

## typed/builtin//modules/api/engine/text/text/rasterize {#typed-builtin-modules-api-engine-text-text-rasterize}

```lua
text.rasterize(handle: any?, texture: any?, scale: number?) -> any
```

Rasterise the handle's current text + style into the given runtime GPU
texture. Bind that texture's guid as a material's `base_color_texture` to
display the text; re-rasterising the same texture overwrites it in place.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.
- `texture` `any` _(optional)_ — Destination GPU texture handle (`renderer.texture.create`) or its
guid string — WHERE the raster lands.
- `scale` `number` _(optional)_ — World/pixel scale factor for the raster (default 1.0).

**Returns** `any` — Table with `width` and `height` (in pixels), or nil if nothing rasterised.

```lua
local tex = renderer.texture.create({ width = 256, height = 64 })
local r = text.rasterize(h, tex, 1.0)
```

## typed/builtin//modules/api/engine/text/text/setStyle {#typed-builtin-modules-api-engine-text-text-setstyle}

```lua
text.setStyle(handle: any?, style: table) -> boolean
```

Replace the handle's style. Fields not present keep their current
value.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.
- `style` `table` — Style table (fontSize, color, alignment, outline, ...).

**Returns** `boolean` — True when the text system held the handle and took the style; false when it did not.

```lua
text.setStyle(h, { fontSize = 64, color = "yellow" })
```

## typed/builtin//modules/api/engine/text/text/setText {#typed-builtin-modules-api-engine-text-text-settext}

```lua
text.setText(handle: any?, content: string) -> boolean
```

Replace the handle's text content.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.
- `content` `string` — New text string.

**Returns** `boolean` — True when the text system held the handle and took the content; false when it did not, which is how a caller learns its handle went away.

```lua
if not text.setText(h, "HP: 100") then h = text.create({ content = "HP: 100" }) end
```

## typed/builtin//modules/api/engine/text/text/textureGuid {#typed-builtin-modules-api-engine-text-text-textureguid}

```lua
text.textureGuid(handle: any?) -> string?
```

The runtime GPU texture guid this handle rasterises into — bind it as a
material texture (`base_color_texture`) to display the text.

**Parameters**

- `handle` `any` _(optional)_ — Text handle from `text.create`.

**Returns** `string?` — The texture guid string, or nil for a handle the text system does not hold.

```lua
entity(id).component.get("Material"):setTexture("base_color_texture", text.textureGuid(h))
```
