Render Descriptors
Renderable types attached to entities: sprite, tiles, panel, box, circle, text, mesh, and GPU particles.
Each entity can have multiple renderables. A renderable is created by passing a RenderDescriptor object to Entity.createRenderable() or included in the renderable array at spawn time. The type field determines which properties are used.
Render descriptors describe world renderables. To see them in a window, the board containing the entities must be attached to that window with Board.attach(...) or Board.attachOffset(...).
Shared fields #
typestring — One of"sprite","tiles","panel","box","circle","text","mesh","particles".layernumber — Draw order. Higher layers render on top.texturestring optional — Asset path to the texture (e.g./rom/sprites/atlas.png).textureSemanticTextureSemantic optional — Texture load semantic. Default'color-srgb-premul'; use'data-linear-no-premul'for normals/height/data textures. Alias:textureLoad.fontstring optional — Asset path to a TTF font (text type only).shader{ vert: string, frag: string, blend?: BlendMode } optional — Optional SPIR-V shader override.blendmay also be provided asblendMode; default is'premultiplied-alpha'.scalePoint optional — Render scale{ x, y }. Defaults to{ x: 1, y: 1 }.
Shader blend semantics #
shader.blend selects the Vulkan colour blend state for that shader pipeline. It only applies when a custom shader object is supplied. Omit it to use the engine default, 'premultiplied-alpha'.
'premultiplied-alpha'— default Koya/Helix behaviour. Fragment output is expected to be premultiplied: RGB has already been multiplied by A.'alpha'— straight alpha blending. Use this when your shader outputs ordinary non-premultiplied RGB plus alpha.'additive'— additive colour accumulation. Useful for glow, fire, particles, and light contribution style passes.'opaque'— disables colour blending. Alpha does not make the fragment transparent.
{
type: 'mesh',
layer: 10,
shader: {
vert: '/rom/shaders/vertex_color.vert.spv',
frag: '/rom/shaders/vertex_color.frag.spv',
blend: 'additive'
},
geometry: {
vertex: [
{ pos: [-1, -1, 0], color: [0.8, 0.1, 0.0, 0.6], uv: [0, 0] },
{ pos: [ 1, -1, 0], color: [0.8, 0.1, 0.0, 0.6], uv: [1, 0] },
{ pos: [ 1, 1, 0], color: [0.8, 0.1, 0.0, 0.6], uv: [1, 1] },
{ pos: [-1, 1, 0], color: [0.8, 0.1, 0.0, 0.6], uv: [0, 1] }
],
indices: [0, 1, 2, 2, 3, 0]
}
}Texture load semantics #
textureSemantic controls how an image asset is uploaded when a render descriptor loads texture. It is a load-time semantic, not a sampler or shader switch. If the same texture path has already been loaded in the window texture pool, the existing texture is reused.
'color-srgb-premul'— default. Uploads as SRGB colour and premultiplies RGBA pixels for the default renderer blend path.'data-linear-no-premul'— uploads as linear UNORM data and does not premultiply. Use this for normal maps, height maps, masks, lookup tables, and other non-colour data.
{
type: 'sprite',
texture: '/rom/textures/normal.png',
textureSemantic: 'data-linear-no-premul',
frames: [{
size: { x: 2, y: 2 },
origin: { x: 1, y: 1 },
aabb: { min: { x: 0, y: 0 }, max: { x: 64, y: 64 } }
}]
}If you need the same asset path with different texture semantics in one window, load it under distinct virtual paths or keys so the texture pool does not reuse the first upload.
Sprite #
A textured quad with one or more frames for animation. Each frame defines a sub-rectangle of the texture atlas.
type: "sprite".framesSpriteFrame[] — Array of frames defining the sprite sheet.
sizePoint — Display size in world units. Negative width flips horizontally.originPoint — Anchor point relative to the frame (pixels from top-left of the source rect).aabbAABB — Source rectangle in the texture:{ min: { x, y }, max: { x, y } }in pixels.colourColour optional — Tint colour{ r, g, b, a }(0-255). Defaults to white.
{
type: 'sprite',
texture: '/rom/sprites/hero.png',
textureSemantic: 'color-srgb-premul',
layer: 100,
shader: {
vert: '/rom/shaders/sprite.vert.spv',
frag: '/rom/shaders/sprite.frag.spv',
blend: 'premultiplied-alpha'
},
frames: [
{
size: { x: 16, y: 16 },
origin: { x: 8, y: 16 },
aabb: { min: { x: 0, y: 0 }, max: { x: 16, y: 16 } }
},
{
size: { x: 16, y: 16 },
origin: { x: 8, y: 16 },
aabb: { min: { x: 16, y: 0 }, max: { x: 32, y: 16 } }
}
]
}Tiles #
A grid of tiles rendered from a tileset texture. Tile IDs are 1-based (0 = empty). The tile at ID n maps to position (n-1) in the tileset, laid out left-to-right, top-to-bottom.
type: "tiles".sizePoint — Display size of each tile in world units{ x, y }.textureSizePoint — Size of each cell in the tileset texture (pixels){ x, y }.mapSizePoint — Grid dimensions{ x: columns, y: rows }.tileDatanumber[] — Row-major array of uint16 tile IDs.
{
type: 'tiles',
texture: '/rom/tilesets/overworld.png',
layer: 0,
size: { x: 16, y: 16 },
textureSize: { x: 16, y: 16 },
mapSize: { x: 32, y: 32 },
tileData: [1, 2, 3, 0, 0, ...]
}Panel #
A 9-slice or stretch-mode textured panel. Uses the same frames format as sprites for the source rectangle, plus optional mode and edge properties.
type: "panel".framesSpriteFrame[] — Source frames (same format as sprite frames).modestring optional — Panel rendering mode string.edgenumber[] optional — Four-element array[top, right, bottom, left]defining 9-slice insets in pixels.
Box #
A solid-colour rectangle, optionally with rounded corners.
type: "box".aabbAABB — Rectangle bounds{ min: { x, y }, max: { x, y } }in world units.colourColour — Fill colour{ r, g, b, a }(0-255).cornerRadiusnumber optional — Radius for rounded corners.cornerResolutionnumber optional — Number of segments per rounded corner.insetnumber optional — Inner inset for rendering a rectangular border instead of a filled rectangle.
{
type: 'box',
layer: 50,
aabb: { min: { x: -8, y: -8 }, max: { x: 8, y: 8 } },
colour: { r: 255, g: 0, b: 0, a: 200 },
cornerRadius: 2,
cornerResolution: 4
}Circle #
A solid-colour circle, ellipse, ring, or arc fitted to an AABB.
type: "circle".aabbAABB — Ellipse bounds{ min: { x, y }, max: { x, y } }in world units.colourColour optional — Fill colour{ r, g, b, a }(0-255). Defaults to white.resolutionnumber optional — Number of sides used to approximate the shape. Defaults to 32; minimum is 3.insetnumber optional — Inner inset for rendering a ring instead of a filled circle or ellipse.startnumber optional — Start angle in degrees. Defaults to 0.endnumber optional — End angle in degrees. Defaults to 360.
{
type: 'circle',
layer: 60,
aabb: { min: { x: -8, y: -8 }, max: { x: 8, y: 8 } },
colour: { r: 80, g: 190, b: 255, a: 220 },
resolution: 48,
inset: 2,
start: 0,
end: 270
}Text #
A rendered text string using a TTF font.
type: "text".stringstring — The text to render.sizenumber optional — Font size.colourColour optional — Text colour{ r, g, b, a }(0-255).stylestring optional — Font style string.justifystring optional —"left"(default),"centre", or"right".
{
type: 'text',
font: '/rom/fonts/mono.ttf',
layer: 200,
string: 'Score: 0',
size: 12,
colour: { r: 255, g: 255, b: 255, a: 255 },
justify: 'left'
}Particles #
A modular GPU-simulated world-space emitter. main, emission, shape, motion, and appearance modules live under config; available shapes include point, line, box/edge, and ellipse/ring/arc. Emitters start immediately, particles detach from the entity at spawn, and all world particles draw at the scene/UI boundary. See GPU Particles for curves, gradients, coherent noise, runtime controls, ordering, lifecycle, capacity, and limitations.
type: "particles".configParticleEmitterConfig optional — Complete emitter configuration. See GPU Particles.
Mesh #
A custom mesh rendered from explicit vertices and indices. Useful for debug overlays, procedural shapes, and line-strip style geometry.
type: "mesh".geometryMeshGeometry — Vertex/index data for the mesh.texturestring optional — Optional texture path.
vertexMeshVertex[] | number[] — Either an array of vertex objects or a flat number array in groups of 9:[x, y, z, r, g, b, a, u, v].indicesnumber[] optional — Triangle index list (uint16). Omit for non-indexed geometry.
posnumber[] optional — Position[x, y, z?]. You can also usex,y,zfields.colornumber[] optional — Colour[r, g, b, a?]in normalized 0-1 values. Alias:colour.uvnumber[] optional — Texture UV[u, v].
{
type: 'mesh',
layer: 120,
geometry: {
vertex: [
{ pos: [0, 0, 0], color: [0.0, 1.0, 0.0, 1.0], uv: [0, 0] },
{ pos: [1, 0, 0], color: [0.0, 1.0, 0.0, 1.0], uv: [1, 0] },
{ pos: [0, 1, 0], color: [0.0, 1.0, 0.0, 1.0], uv: [0, 1] }
],
indices: [0, 1, 2]
}
}