
# tecs.gfx.particles


GPU particle effects, emitter playback, pool sizing, and rendering limits.

An entity represents an emitter. The GPU owns individual particles, so game
code controls an effect and its emitter rather than inspecting particles one
at a time.

```teal
local sparks <const> = tecs.gfx.particles.newEffect({
    name = "game.sparks",
    capacity = 256,
    schedule = {rate = 120, duration = 0.3},
    spawn = {shape = "disc", width = 4, outward = true},
    initial = {
        lifetime = {min = 0.2, max = 0.5},
        speed = {min = 40, max = 120},
        size = 3,
    },
    update = {
        drag = 2.0,
        size = tecs.gfx.particles.newCurve({
            {0.0, 1.0},
            {1.0, 0.0},
        }),
    },
})

world:addPlugin(tecs.gfx.particles.plugin({
    renderer = app.renderer,
    capacity = 4096,
}))

world:spawn(
    tecs.Transform2D(120, 80, 0, 1, 0, 1, 1),
    tecs.gfx.particles.ParticleEmitter({effect = sparks})
)
```

An immutable [`Effect`](/modules/gfx/particles/#tecs.gfx.particles.Effect) describes schedule,
spawn, initial state, updates, and rendering. A
[`ParticleEmitter`](/modules/gfx/particles/#tecs.gfx.particles.ParticleEmitter) names the
effect and carries playback state. Effect names form a snapshot compatibility
surface.

## Emission and motion

Schedules combine rate, delay, duration, looping, and timed bursts.
`duration = 0` keeps a cycle open. An emission that finds no free effect slot
either drops the new particle or replaces the oldest, according to `overflow`.

Spawn shapes include point, line, rectangle, rectangle edge, disc, ring, and
cone. World-space particles detach from later emitter movement. Local-space
particles follow the emitter for their full life. `inheritVelocity` applies
only in world space.

Curves and gradients sample normalized age from zero to one. Numeric properties
accept a constant or a `{min, max}` range.

## Playback

`play` starts or resumes emission. `stop` resets the schedule while live
particles drain. `pause` holds the schedule and live field. `clear` kills the
field without changing emission. `restart` resets schedule and randomness.
`burst` queues a count for the next step.

`finished` derives its answer from the schedule and the longest lifetime.
`estimatedCount` integrates the schedule over the mean lifetime, and may differ
after overflow or a truncated burst. Game code cannot inspect, move, kill, or
count individual particles.

## Pool and snapshots

The plugin fixes world capacity and maximum emitters at installation. Each
emitter reserves its effect capacity. An emitter that cannot fit draws nothing
and logs the refusal.

Snapshots store effect name, seed, configuration, and playback state. They do
not store individual particles, so a restored emitter starts empty and refills.

## Blending

`render.blend` selects `"alpha"`, `"additive"`, or `"opaque"`, and defaults to
`"alpha"`. The first two draw in the forward pass over the composited image,
depth tested and not written, so alpha means what it says: a gradient that ends
transparent fades out, and `"additive"` adds light instead of covering, which is
what fire, glow, and sparks want. `"opaque"` keeps the effect in the G-buffer.

A blended effect is not in the G-buffer, so it casts no shadow and no light's
occluder mask sees it, and it is not shadowed by one either. It also draws after
compositing, so all of one effect's particles composite as one group in pool-slot
order rather than being sorted against each other: an effect names one layer and
its particles take one depth within it. Sorting within a pool is a separate
feature.

One blended emitter puts the frame's forward lane to work, which is five compute
passes and a draw a world with nothing blended in it does not pay. A world of
`"opaque"` effects pays none of it, which is the other reason that name exists.

## Module contents

### Constructors

| Constructor | Description |
| --- | --- |
| [`newCurve`](/modules/gfx/particles/#tecs.gfx.particles.newCurve) | Compiles keyframes into a curve over normalized age. |
| [`newEffect`](/modules/gfx/particles/#tecs.gfx.particles.newEffect) | Registers an immutable effect under options.name and shares it among every emitter using that name. |
| [`newGradient`](/modules/gfx/particles/#tecs.gfx.particles.newGradient) | Compiles keyframes into a color gradient over normalized age. |

### Types

| Type | Kind | Description |
| --- | --- | --- |
| [`Color`](/modules/gfx/particles/#tecs.gfx.particles.Color) | <span class="tealdoc-kind-badge tealdoc-kind-type">type</span> | Represents a color accepted by particle authoring fields. |
| [`Curve`](/modules/gfx/particles/#tecs.gfx.particles.Curve) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Stores a compiled scalar curve. |
| [`Draining`](/modules/gfx/particles/#tecs.gfx.particles.Draining) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | A slot range whose emitter is gone, held until its last particle can no longer be alive. |
| [`Effect`](/modules/gfx/particles/#tecs.gfx.particles.Effect) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | The handle newEffect returns and a ParticleEmitter names. |
| [`EffectOptions`](/modules/gfx/particles/#tecs.gfx.particles.EffectOptions) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Configures newEffect. |
| [`EmitterOptions`](/modules/gfx/particles/#tecs.gfx.particles.EmitterOptions) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Configures a ParticleEmitter. |
| [`EmitterState`](/modules/gfx/particles/#tecs.gfx.particles.EmitterState) | <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span> | The three values an emitter's state takes. |
| [`Gradient`](/modules/gfx/particles/#tecs.gfx.particles.Gradient) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Stores a compiled color gradient. |
| [`Holding`](/modules/gfx/particles/#tecs.gfx.particles.Holding) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | One emitter's place in the pool, as the pool remembers it. |
| [`InitialOptions`](/modules/gfx/particles/#tecs.gfx.particles.InitialOptions) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Configures a particle's initial properties. |
| [`ParticleEmitter`](/modules/gfx/particles/#tecs.gfx.particles.ParticleEmitter) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Controls an effect's playback state and per-instance scales. |
| [`Pool`](/modules/gfx/particles/#tecs.gfx.particles.Pool) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Represents a world's installed particle pool. |
| [`PoolOptions`](/modules/gfx/particles/#tecs.gfx.particles.PoolOptions) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Configures plugin. |
| [`Range`](/modules/gfx/particles/#tecs.gfx.particles.Range) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | The two-bound form of a Value. |
| [`RenderOptions`](/modules/gfx/particles/#tecs.gfx.particles.RenderOptions) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Configures how particles draw. |
| [`ScheduleOptions`](/modules/gfx/particles/#tecs.gfx.particles.ScheduleOptions) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Configures when particles emit. |
| [`SpawnOptions`](/modules/gfx/particles/#tecs.gfx.particles.SpawnOptions) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Configures where particles spawn. |
| [`UpdateOptions`](/modules/gfx/particles/#tecs.gfx.particles.UpdateOptions) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Configures how a particle changes over its normalized lifetime. |
| [`Value`](/modules/gfx/particles/#tecs.gfx.particles.Value) | <span class="tealdoc-kind-badge tealdoc-kind-type">type</span> | Accepted wherever a property may vary per particle. |

### Functions

| Function | Kind | Description |
| --- | --- | --- |
| [`find`](/modules/gfx/particles/#tecs.gfx.particles.find) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the effect registered under name, or nil when none exists. |
| [`names`](/modules/gfx/particles/#tecs.gfx.particles.names) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns every registered effect name in registration order. |
| [`plugin`](/modules/gfx/particles/#tecs.gfx.particles.plugin) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Installs the pool on a world. |
| [`poolOf`](/modules/gfx/particles/#tecs.gfx.particles.poolOf) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the pool installed on a world, or nil. |
| [`reset`](/modules/gfx/particles/#tecs.gfx.particles.reset) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Forgets every registered effect, so a spec can register a set again. |

### Values

| Value | Type | Description |
| --- | --- | --- |
| [`CURVE_SAMPLES`](/modules/gfx/particles/#tecs.gfx.particles.CURVE_SAMPLES) | `integer` | Read-only. Reports how many samples one compiled curve or gradient holds. |
| [`EFFECT`](/modules/gfx/particles/#tecs.gfx.particles.EFFECT) | `{string : integer}` | Read-only. Reports each field's offset within an effect record. |
| [`EFFECT_FLOATS`](/modules/gfx/particles/#tecs.gfx.particles.EFFECT_FLOATS) | `integer` | Read-only. Reports how many floats each effect record contains. |
| [`EMITTER`](/modules/gfx/particles/#tecs.gfx.particles.EMITTER) | `{string : integer}` | Read-only. Reports each field's float offset within an emitter record, counting from zero. |
| [`EMITTER_FLOATS`](/modules/gfx/particles/#tecs.gfx.particles.EMITTER_FLOATS) | `integer` | Read-only. Reports how many floats each emitter record contains. |
| [`STATE_FLOATS`](/modules/gfx/particles/#tecs.gfx.particles.STATE_FLOATS) | `integer` | Read-only. Reports how many GPU state floats one particle carries. |

## Constructors

<a id="tecs.gfx.particles.newCurve"></a>
### tecs.gfx.particles.newCurve <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Compiles keyframes into a curve over normalized age.



```teal
function tecs.gfx.particles.newCurve(keys: {CurveKey}): Curve
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `keys` | `{CurveKey}` | Supplies `{age, value}` pairs with age from zero through one, in any order. Empty or nil raises, and one key defines a constant. |

#### Returns

| Type | Description |
| --- | --- |
| [`Curve`](/modules/gfx/particles/#tecs.gfx.particles.Curve) | Returns the piecewise-linear curve resampled at `CURVE_SAMPLES` points. Ages outside zero through one read as the nearest endpoint. |

<a id="tecs.gfx.particles.newEffect"></a>
### tecs.gfx.particles.newEffect <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Registers an immutable effect under `options.name` and shares it among
every emitter using that name. The function requires a unique name.



```teal
function tecs.gfx.particles.newEffect(options: EffectOptions): Effect
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `options` | [`EffectOptions`](/modules/gfx/particles/#tecs.gfx.particles.EffectOptions) | Every section but `name` is optional and defaults to something drawable. |

#### Returns

| Type | Description |
| --- | --- |
| [`Effect`](/modules/gfx/particles/#tecs.gfx.particles.Effect) | Returns a handle that stays valid until `reset`. The process-wide registry makes it reachable from every world. |

<a id="tecs.gfx.particles.newGradient"></a>
### tecs.gfx.particles.newGradient <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Compiles keyframes into a color gradient over normalized age.



```teal
function tecs.gfx.particles.newGradient(keys: {CurveKey}): Gradient
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `keys` | `{CurveKey}` | Supplies `{age, color}` pairs under the same rules as `newCurve`. Alpha fades a blended effect and is ignored by an opaque one. |

#### Returns

| Type | Description |
| --- | --- |
| [`Gradient`](/modules/gfx/particles/#tecs.gfx.particles.Gradient) | A gradient resampled like a curve, interpolating each channel separately, so two colors blend through whatever lies between them componentwise rather than around a color wheel. |

## Types

<a id="tecs.gfx.particles.Color"></a>
### tecs.gfx.particles.Color <span class="tealdoc-kind-badge tealdoc-kind-type">type</span>

Represents a color accepted by particle authoring fields.


```teal
type tecs.gfx.particles.Color = Color
```

<a id="tecs.gfx.particles.Curve"></a>
### tecs.gfx.particles.Curve <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Stores a compiled scalar curve.


```teal
record tecs.gfx.particles.Curve
    samples: {number}
end
```

<a id="tecs.gfx.particles.Curve.samples"></a>
#### tecs.gfx.particles.Curve.samples <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Provides evenly spaced samples over normalized age from zero
to one.


```teal
tecs.gfx.particles.Curve.samples: {number}
```

<a id="tecs.gfx.particles.Draining"></a>
### tecs.gfx.particles.Draining <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

A slot range whose emitter is gone, held until its last particle can no
longer be alive.


```teal
global record tecs.gfx.particles.Draining
    base: integer
    count: integer
    releaseAt: number
    blended: boolean
end
```

<a id="tecs.gfx.particles.Draining.base"></a>
#### tecs.gfx.particles.Draining.base <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the first particle slot awaiting release.


```teal
tecs.gfx.particles.Draining.base: integer
```

<a id="tecs.gfx.particles.Draining.count"></a>
#### tecs.gfx.particles.Draining.count <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the number of particle slots awaiting release.


```teal
tecs.gfx.particles.Draining.count: integer
```

<a id="tecs.gfx.particles.Draining.releaseAt"></a>
#### tecs.gfx.particles.Draining.releaseAt <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the time when the slots become reusable.


```teal
tecs.gfx.particles.Draining.releaseAt: number
```

<a id="tecs.gfx.particles.Draining.blended"></a>
#### tecs.gfx.particles.Draining.blended <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Reports whether the effect that held this range blends.
The pool carries this value because the emitter and its effect are
gone while the range's particles are still being drawn.


```teal
tecs.gfx.particles.Draining.blended: boolean
```

<a id="tecs.gfx.particles.Effect"></a>
### tecs.gfx.particles.Effect <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

The handle `newEffect` returns and a
[`ParticleEmitter`](/modules/gfx/particles/#tecs.gfx.particles.ParticleEmitter) names.


```teal
record tecs.gfx.particles.Effect
    name: string
    index: integer
    capacity: integer
    blend: string
    maxLifetime: number
    meanLifetime: number
    emitFor: number
    rate: number
    delay: number
    duration: number
    looping: boolean
    bursts: {number}
end
```

<a id="tecs.gfx.particles.Effect.name"></a>
#### tecs.gfx.particles.Effect.name <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the effect name and the only property that outlives the
process. Everything a save, a tool or a person identifies an effect by
is this.


```teal
tecs.gfx.particles.Effect.name: string
```

<a id="tecs.gfx.particles.Effect.index"></a>
#### tecs.gfx.particles.Effect.index <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports where its record sits in the assembled effect table, counting from one.
A position in registration order, so it means nothing outside the
process that assigned it and nothing stores one.


```teal
tecs.gfx.particles.Effect.index: integer
```

<a id="tecs.gfx.particles.Effect.capacity"></a>
#### tecs.gfx.particles.Effect.capacity <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports how many live particles one emitter can hold.


```teal
tecs.gfx.particles.Effect.capacity: integer
```

<a id="tecs.gfx.particles.Effect.blend"></a>
#### tecs.gfx.particles.Effect.blend <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the blend mode the effect resolved to, which is one of
the three `render.blend` names and never nil. The pool reads it to say how
many of its slots may reach the forward pass.


```teal
tecs.gfx.particles.Effect.blend: string
```

<a id="tecs.gfx.particles.Effect.maxLifetime"></a>
#### tecs.gfx.particles.Effect.maxLifetime <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the longest particle lifetime in seconds, which decides when an
emitter that has stopped emitting has finished.


```teal
tecs.gfx.particles.Effect.maxLifetime: number
```

<a id="tecs.gfx.particles.Effect.meanLifetime"></a>
#### tecs.gfx.particles.Effect.meanLifetime <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the mean particle lifetime in seconds, halfway between the bounds
`initial.lifetime` was authored with. This is the window
`estimatedCount` integrates the schedule over, and it is the steady-state
live count divided by the emission rate.


```teal
tecs.gfx.particles.Effect.meanLifetime: number
```

<a id="tecs.gfx.particles.Effect.emitFor"></a>
#### tecs.gfx.particles.Effect.emitFor <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports seconds from `play` to the last emission, or -1 for an effect that
never stops emitting.


```teal
tecs.gfx.particles.Effect.emitFor: number
```

<a id="tecs.gfx.particles.Effect.rate"></a>
#### tecs.gfx.particles.Effect.rate <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the continuous emission rate used by `finished` and
`estimatedCount`.


```teal
tecs.gfx.particles.Effect.rate: number
```

<a id="tecs.gfx.particles.Effect.delay"></a>
#### tecs.gfx.particles.Effect.delay <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the emission delay in seconds.


```teal
tecs.gfx.particles.Effect.delay: number
```

<a id="tecs.gfx.particles.Effect.duration"></a>
#### tecs.gfx.particles.Effect.duration <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports the emission duration in seconds.


```teal
tecs.gfx.particles.Effect.duration: number
```

<a id="tecs.gfx.particles.Effect.looping"></a>
#### tecs.gfx.particles.Effect.looping <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports whether the emission cycle repeats.


```teal
tecs.gfx.particles.Effect.looping: boolean
```

<a id="tecs.gfx.particles.Effect.bursts"></a>
#### tecs.gfx.particles.Effect.bursts <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Provides the compiled burst schedule.


```teal
tecs.gfx.particles.Effect.bursts: {number}
```

<a id="tecs.gfx.particles.EffectOptions"></a>
### tecs.gfx.particles.EffectOptions <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Configures `newEffect`.


```teal
record tecs.gfx.particles.EffectOptions
    name: string
    capacity: integer
    overflow: string
    schedule: ScheduleOptions
    spawn: SpawnOptions
    initial: InitialOptions
    update: UpdateOptions
    render: RenderOptions
end
```

<a id="tecs.gfx.particles.EffectOptions.name"></a>
#### tecs.gfx.particles.EffectOptions.name <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the required process-wide unique effect name.
Snapshots use this name to identify the effect.


```teal
tecs.gfx.particles.EffectOptions.name: string
```

<a id="tecs.gfx.particles.EffectOptions.capacity"></a>
#### tecs.gfx.particles.EffectOptions.capacity <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets how many live particles one emitter can hold.
Defaults to 256, and under one raises. A steady-state emitter needs at
least `rate * maxLifetime`; a smaller capacity registers, drops the
emissions that find no slot, and logs a warning saying so, because a
spray thinner than the one authored is otherwise found by eye.


```teal
tecs.gfx.particles.EffectOptions.capacity: integer
```

<a id="tecs.gfx.particles.EffectOptions.overflow"></a>
#### tecs.gfx.particles.EffectOptions.overflow <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects `"drop"` or `"replace"` when an emission arrives
with no free slot: give way, or take the oldest live particle's. Defaults
to `"replace"`, and anything else raises.


```teal
tecs.gfx.particles.EffectOptions.overflow: string
```

<a id="tecs.gfx.particles.EffectOptions.schedule"></a>
#### tecs.gfx.particles.EffectOptions.schedule <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Configures when the effect emits particles. An absent
section takes every default below it.


```teal
tecs.gfx.particles.EffectOptions.schedule: ScheduleOptions
```

<a id="tecs.gfx.particles.EffectOptions.spawn"></a>
#### tecs.gfx.particles.EffectOptions.spawn <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Configures where the effect spawns particles. An absent
section takes every default below it.


```teal
tecs.gfx.particles.EffectOptions.spawn: SpawnOptions
```

<a id="tecs.gfx.particles.EffectOptions.initial"></a>
#### tecs.gfx.particles.EffectOptions.initial <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Configures each particle's initial properties. An absent
section takes every default below it.


```teal
tecs.gfx.particles.EffectOptions.initial: InitialOptions
```

<a id="tecs.gfx.particles.EffectOptions.update"></a>
#### tecs.gfx.particles.EffectOptions.update <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Configures how particles change over their lifetime. An
absent section leaves a particle on its launch velocity.


```teal
tecs.gfx.particles.EffectOptions.update: UpdateOptions
```

<a id="tecs.gfx.particles.EffectOptions.render"></a>
#### tecs.gfx.particles.EffectOptions.render <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Configures how particles draw. An absent section draws
white quads on layer one.


```teal
tecs.gfx.particles.EffectOptions.render: RenderOptions
```

<a id="tecs.gfx.particles.EmitterOptions"></a>
### tecs.gfx.particles.EmitterOptions <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Configures a
[`ParticleEmitter`](/modules/gfx/particles/#tecs.gfx.particles.ParticleEmitter).


```teal
record tecs.gfx.particles.EmitterOptions
    effect: Effect
    state: EmitterState
    seed: number
    rateScale: number
    sizeScale: number
    timeScale: number
    tint: Color
end
```

<a id="tecs.gfx.particles.EmitterOptions.effect"></a>
#### tecs.gfx.particles.EmitterOptions.effect <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects the required effect. An absent one raises.


```teal
tecs.gfx.particles.EmitterOptions.effect: Effect
```

<a id="tecs.gfx.particles.EmitterOptions.state"></a>
#### tecs.gfx.particles.EmitterOptions.state <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the initial playback state. Defaults to `"playing"`,
and anything outside the three raises.


```teal
tecs.gfx.particles.EmitterOptions.state: EmitterState
```

<a id="tecs.gfx.particles.EmitterOptions.seed"></a>
#### tecs.gfx.particles.EmitterOptions.seed <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the random seed. Defaults to one.


```teal
tecs.gfx.particles.EmitterOptions.seed: number
```

<a id="tecs.gfx.particles.EmitterOptions.rateScale"></a>
#### tecs.gfx.particles.EmitterOptions.rateScale <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Multiplies the effect's emission rate. Defaults to one.


```teal
tecs.gfx.particles.EmitterOptions.rateScale: number
```

<a id="tecs.gfx.particles.EmitterOptions.sizeScale"></a>
#### tecs.gfx.particles.EmitterOptions.sizeScale <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Multiplies every particle's size. Defaults to one.


```teal
tecs.gfx.particles.EmitterOptions.sizeScale: number
```

<a id="tecs.gfx.particles.EmitterOptions.timeScale"></a>
#### tecs.gfx.particles.EmitterOptions.timeScale <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Multiplies the speed of the effect clock. Defaults to
one.


```teal
tecs.gfx.particles.EmitterOptions.timeScale: number
```

<a id="tecs.gfx.particles.EmitterOptions.tint"></a>
#### tecs.gfx.particles.EmitterOptions.tint <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Multiplies every particle's color. Defaults to none,
which multiplies by opaque white.


```teal
tecs.gfx.particles.EmitterOptions.tint: Color
```

<a id="tecs.gfx.particles.EmitterState"></a>
### tecs.gfx.particles.EmitterState <span class="tealdoc-kind-badge tealdoc-kind-enum">enum</span>

The three values an emitter's `state` takes.


```teal
global enum tecs.gfx.particles.EmitterState
    "paused"
    "playing"
    "stopped"
end
```

<a id="tecs.gfx.particles.Gradient"></a>
### tecs.gfx.particles.Gradient <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Stores a compiled color gradient.


```teal
record tecs.gfx.particles.Gradient
    samples: {number}
end
```

<a id="tecs.gfx.particles.Gradient.samples"></a>
#### tecs.gfx.particles.Gradient.samples <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Provides evenly spaced RGBA samples over normalized age,
with four floats per sample.


```teal
tecs.gfx.particles.Gradient.samples: {number}
```

<a id="tecs.gfx.particles.Holding"></a>
### tecs.gfx.particles.Holding <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

One emitter's place in the pool, as the pool remembers it.


```teal
global record tecs.gfx.particles.Holding
    item: ParticleEmitter
    index: integer
    base: integer
    count: integer
    tint: Color
    tintR: number
    tintG: number
    tintB: number
    tintA: number
end
```

<a id="tecs.gfx.particles.Holding.item"></a>
#### tecs.gfx.particles.Holding.item <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the emitter assigned to this allocation.


```teal
tecs.gfx.particles.Holding.item: ParticleEmitter
```

<a id="tecs.gfx.particles.Holding.index"></a>
#### tecs.gfx.particles.Holding.index <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the reusable emitter index.


```teal
tecs.gfx.particles.Holding.index: integer
```

<a id="tecs.gfx.particles.Holding.base"></a>
#### tecs.gfx.particles.Holding.base <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the first particle slot.


```teal
tecs.gfx.particles.Holding.base: integer
```

<a id="tecs.gfx.particles.Holding.count"></a>
#### tecs.gfx.particles.Holding.count <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the number of particle slots.


```teal
tecs.gfx.particles.Holding.count: integer
```

<a id="tecs.gfx.particles.Holding.tint"></a>
#### tecs.gfx.particles.Holding.tint <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the tint as it was last read and what it parsed
to. Parsing a color string per emitter per frame would be the one
allocation in a loop that otherwise has none, and a tint changes
rarely.


```teal
tecs.gfx.particles.Holding.tint: Color
```

<a id="tecs.gfx.particles.Holding.tintR"></a>
#### tecs.gfx.particles.Holding.tintR <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the parsed red tint channel.


```teal
tecs.gfx.particles.Holding.tintR: number
```

<a id="tecs.gfx.particles.Holding.tintG"></a>
#### tecs.gfx.particles.Holding.tintG <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the parsed green tint channel.


```teal
tecs.gfx.particles.Holding.tintG: number
```

<a id="tecs.gfx.particles.Holding.tintB"></a>
#### tecs.gfx.particles.Holding.tintB <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the parsed blue tint channel.


```teal
tecs.gfx.particles.Holding.tintB: number
```

<a id="tecs.gfx.particles.Holding.tintA"></a>
#### tecs.gfx.particles.Holding.tintA <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Engine-owned. Stores the parsed alpha tint channel.


```teal
tecs.gfx.particles.Holding.tintA: number
```

<a id="tecs.gfx.particles.InitialOptions"></a>
### tecs.gfx.particles.InitialOptions <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Configures a particle's initial properties.


```teal
global record tecs.gfx.particles.InitialOptions
    lifetime: Value
    speed: Value
    size: Value
    rotation: Value
    angularVelocity: Value
    accelerationX: Value
    accelerationY: Value
    color: Color
end
```

<a id="tecs.gfx.particles.InitialOptions.lifetime"></a>
#### tecs.gfx.particles.InitialOptions.lifetime <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets particle lifetime in seconds. Defaults to one, and
its upper bound is the effect's `maxLifetime`.


```teal
tecs.gfx.particles.InitialOptions.lifetime: Value
```

<a id="tecs.gfx.particles.InitialOptions.speed"></a>
#### tecs.gfx.particles.InitialOptions.speed <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets initial speed in world units per second. Defaults to
zero.


```teal
tecs.gfx.particles.InitialOptions.speed: Value
```

<a id="tecs.gfx.particles.InitialOptions.size"></a>
#### tecs.gfx.particles.InitialOptions.size <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets initial size in world units. Defaults to one.


```teal
tecs.gfx.particles.InitialOptions.size: Value
```

<a id="tecs.gfx.particles.InitialOptions.rotation"></a>
#### tecs.gfx.particles.InitialOptions.rotation <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets initial rotation in radians. Defaults to zero.


```teal
tecs.gfx.particles.InitialOptions.rotation: Value
```

<a id="tecs.gfx.particles.InitialOptions.angularVelocity"></a>
#### tecs.gfx.particles.InitialOptions.angularVelocity <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets initial angular velocity in radians per second.
Defaults to zero.


```teal
tecs.gfx.particles.InitialOptions.angularVelocity: Value
```

<a id="tecs.gfx.particles.InitialOptions.accelerationX"></a>
#### tecs.gfx.particles.InitialOptions.accelerationX <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets constant horizontal acceleration. Independent axes
make a plume drift apart instead of translating as a block. Defaults to
zero.


```teal
tecs.gfx.particles.InitialOptions.accelerationX: Value
```

<a id="tecs.gfx.particles.InitialOptions.accelerationY"></a>
#### tecs.gfx.particles.InitialOptions.accelerationY <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets constant vertical acceleration. Defaults to zero.


```teal
tecs.gfx.particles.InitialOptions.accelerationY: Value
```

<a id="tecs.gfx.particles.InitialOptions.color"></a>
#### tecs.gfx.particles.InitialOptions.color <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the color every particle starts with, multiplied by
the gradient and by the emitter's tint. Defaults to opaque white.


```teal
tecs.gfx.particles.InitialOptions.color: Color
```

<a id="tecs.gfx.particles.ParticleEmitter"></a>
### tecs.gfx.particles.ParticleEmitter <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Controls an effect's playback state and per-instance scales.

Deliberately small. If an instance could override every effect field then
effects would stop being reusable GPU data and every emitter would grow
to hold a copy. The effect owns capacity because it determines the pool
slot range when the emitter starts.
Read-only. Exposes the particle-emitter component.


```teal
record tecs.gfx.particles.ParticleEmitter is ecs.Component
    effect: Effect
    state: EmitterState
    seed: number
    rateScale: number
    sizeScale: number
    timeScale: number
    tint: Color

    burst: function(self, count: number)
    clear: function(self)
    estimatedCount: function(self): integer
    finished: function(self): boolean
    pause: function(self)
    play: function(self)
    restart: function(self)
    stop: function(self)
end
```

#### Interfaces

| Interface |
| --- |
| [`ecs.Component`](/modules/ecs/#tecs.ecs.Component) |

<a id="tecs.gfx.particles.ParticleEmitter.effect"></a>
#### tecs.gfx.particles.ParticleEmitter.effect <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects the effect before spawning the emitter. It
cannot change after spawn because the
slot range is the effect's capacity, and changing it would mean
reallocating. Despawn and spawn again, which is cheap.


```teal
tecs.gfx.particles.ParticleEmitter.effect: Effect
```

<a id="tecs.gfx.particles.ParticleEmitter.state"></a>
#### tecs.gfx.particles.ParticleEmitter.state <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Reports whether the emitter plays, pauses, or stops. Set
it through `play`, `pause` and `stop` rather than by assignment: those
move the cycle's start step and the random generation with it, and a bare
write leaves both where the previous state left them, so an emitter
written back to `"playing"` resumes part way through its schedule instead
of starting it again.


```teal
tecs.gfx.particles.ParticleEmitter.state: EmitterState
```

<a id="tecs.gfx.particles.ParticleEmitter.seed"></a>
#### tecs.gfx.particles.ParticleEmitter.seed <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects the random sequence. Two emitters with one seed
produce one field.


```teal
tecs.gfx.particles.ParticleEmitter.seed: number
```

<a id="tecs.gfx.particles.ParticleEmitter.rateScale"></a>
#### tecs.gfx.particles.ParticleEmitter.rateScale <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Multiplies the effect's emission rate.


```teal
tecs.gfx.particles.ParticleEmitter.rateScale: number
```

<a id="tecs.gfx.particles.ParticleEmitter.sizeScale"></a>
#### tecs.gfx.particles.ParticleEmitter.sizeScale <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Multiplies every particle's size.


```teal
tecs.gfx.particles.ParticleEmitter.sizeScale: number
```

<a id="tecs.gfx.particles.ParticleEmitter.timeScale"></a>
#### tecs.gfx.particles.ParticleEmitter.timeScale <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Multiplies the speed of the effect clock.


```teal
tecs.gfx.particles.ParticleEmitter.timeScale: number
```

<a id="tecs.gfx.particles.ParticleEmitter.tint"></a>
#### tecs.gfx.particles.ParticleEmitter.tint <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Multiplies every particle's color, alpha included, so a
tint is how one emitter of a blended effect fades against another. An
effect whose `render.blend` is `"opaque"` ignores the alpha.


```teal
tecs.gfx.particles.ParticleEmitter.tint: Color
```

<a id="tecs.gfx.particles.ParticleEmitter.burst"></a>
#### tecs.gfx.particles.ParticleEmitter:burst <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Emits `count` particles at the next step boundary.

A stopped emitter ignores the call. The emitter truncates a count beyond
its free capacity to the number that fits.


```teal
function tecs.gfx.particles.ParticleEmitter.burst(self, count: number)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `ParticleEmitter` |  |
| `count` | `number` |  |

##### Returns

None.

<a id="tecs.gfx.particles.ParticleEmitter.clear"></a>
#### tecs.gfx.particles.ParticleEmitter:clear <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Kills every live particle immediately.

Use this for teardown and state transitions. It leaves emission unchanged,
so a playing emitter starts filling again on the next step.


```teal
function tecs.gfx.particles.ParticleEmitter.clear(self)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `ParticleEmitter` |  |

##### Returns

None.

<a id="tecs.gfx.particles.ParticleEmitter.estimatedCount"></a>
#### tecs.gfx.particles.ParticleEmitter:estimatedCount <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Returns an estimate of how many particles remain alive.

Integrates the schedule over the effect's `meanLifetime` and treats every
emission older than that as expired. An effect whose `initial.lifetime` is
one number has a mean equal to that lifetime, so the answer is exact but for
the step the emitter is part way through; a lifetime range makes it an
expectation, because the host does not know which particles drew the short
lifetimes. A truncated burst or an overflow replacement moves it either way.
Reading the exact count back from the GPU would stall the pipeline.


```teal
function tecs.gfx.particles.ParticleEmitter.estimatedCount(
    self
): integer
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `ParticleEmitter` |  |

##### Returns

| Type | Description |
| --- | --- |
| `integer` |  |

<a id="tecs.gfx.particles.ParticleEmitter.finished"></a>
#### tecs.gfx.particles.ParticleEmitter:finished <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Returns whether this emitter has stopped emitting and its last particle has
died.

Exact, and it costs nothing: it is the schedule plus the longest lifetime
against the emitter's own clock, all of which the host holds. This is what
almost every "has the explosion finished" question is actually asking, and
unlike a count it needs nothing read back from the GPU.


```teal
function tecs.gfx.particles.ParticleEmitter.finished(self): boolean
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `ParticleEmitter` |  |

##### Returns

| Type | Description |
| --- | --- |
| `boolean` |  |

<a id="tecs.gfx.particles.ParticleEmitter.pause"></a>
#### tecs.gfx.particles.ParticleEmitter:pause <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Stops emission and holds. The schedule and the particles both keep their
state, and neither ages while this is on.


```teal
function tecs.gfx.particles.ParticleEmitter.pause(self)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `ParticleEmitter` |  |

##### Returns

None.

<a id="tecs.gfx.particles.ParticleEmitter.play"></a>
#### tecs.gfx.particles.ParticleEmitter:play <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Starts or resumes emission.

From stopped this begins the cycle again; from paused it carries on from
where it was, with the steps spent paused not counted against the schedule.


```teal
function tecs.gfx.particles.ParticleEmitter.play(self)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `ParticleEmitter` |  |

##### Returns

None.

<a id="tecs.gfx.particles.ParticleEmitter.restart"></a>
#### tecs.gfx.particles.ParticleEmitter:restart <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Resets the schedule and the random sequence, and starts playing.

Reproducible: a restart of an emitter with the same seed produces the same
field, because every particle's draws are a pure function of the seed, the
generation, the serial and the property, and all four start over.


```teal
function tecs.gfx.particles.ParticleEmitter.restart(self)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `ParticleEmitter` |  |

##### Returns

None.

<a id="tecs.gfx.particles.ParticleEmitter.stop"></a>
#### tecs.gfx.particles.ParticleEmitter:stop <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Stops emission and resets the schedule. Live particles drain.

The next `play` starts the cycle from its beginning with a fresh random
sequence, which is what makes it reproducible. Killing the field instead is
`clear`, and the two are deliberately separate.


```teal
function tecs.gfx.particles.ParticleEmitter.stop(self)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `ParticleEmitter` |  |

##### Returns

None.

<a id="tecs.gfx.particles.Pool"></a>
### tecs.gfx.particles.Pool <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Represents a world's installed particle pool.


```teal
record tecs.gfx.particles.Pool
    capacity: integer

    active: function(self): boolean
    blended: function(self): integer
    casting: function(self): integer
    count: function(self): integer
    destroy: function(self)
    record: function(
        self, frame: Frame, instances: Buffer, bounds: Buffer
    )
    takeDirty: function(self): {integer}
    write: function(
        self,
        floats: loader.CArray,
        bounds: loader.CArray,
        base: integer,
        first: integer,
        last: integer
    )
end
```

<a id="tecs.gfx.particles.Pool.capacity"></a>
#### tecs.gfx.particles.Pool.capacity <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. Reports how many live particles the pool can hold.


```teal
tecs.gfx.particles.Pool.capacity: integer
```

<a id="tecs.gfx.particles.Pool.active"></a>
#### tecs.gfx.particles.Pool:active <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Whether there is anything to dispatch over.

Published rather than derived, and this is the hazard the design named: an
emitter dirties nothing, ever, while its whole field moves every frame.
There is no archetype bit to read and no component write to observe, so a
gate written as "nothing is dirty" would conclude wrongly every frame an
emitter was running. The pool counts its own live emitters instead, and
holds the gate open one frame past the last of them so the pass that hides
their slots is the one that actually runs.


```teal
function tecs.gfx.particles.Pool.active(self): boolean
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Pool` |  |

##### Returns

| Type | Description |
| --- | --- |
| `boolean` |  |

<a id="tecs.gfx.particles.Pool.blended"></a>
#### tecs.gfx.particles.Pool:blended <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Slots the forward pass may find a particle in.

Reserved slots rather than live particles, and it has to be: what is alive
lives in a buffer the CPU never reads, so the honest answer here is the only
one that cannot be too small. Too small is the failure that matters, because
the backend skips the whole forward lane on a frame nothing said was blended
and every particle of every blended effect would then go missing.

A slot with no live particle in it writes the hidden bound, so over-reporting
costs the lane five compute passes over slots the mark pass has already
rejected, and never a draw.


```teal
function tecs.gfx.particles.Pool.blended(self): integer
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Pool` |  |

##### Returns

| Type | Description |
| --- | --- |
| `integer` |  |

<a id="tecs.gfx.particles.Pool.casting"></a>
#### tecs.gfx.particles.Pool:casting <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Particles never write caster lane signs into their bounds.


```teal
function tecs.gfx.particles.Pool.casting(self): integer
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Pool` |  |

##### Returns

| Type | Description |
| --- | --- |
| `integer` |  |

<a id="tecs.gfx.particles.Pool.count"></a>
#### tecs.gfx.particles.Pool:count <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

```teal
function tecs.gfx.particles.Pool.count(self): integer
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Pool` |  |

##### Returns

| Type | Description |
| --- | --- |
| `integer` |  |

<a id="tecs.gfx.particles.Pool.destroy"></a>
#### tecs.gfx.particles.Pool:destroy <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Releases everything the pool owns. Safe to call more than once.


```teal
function tecs.gfx.particles.Pool.destroy(self)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Pool` |  |

##### Returns

None.

<a id="tecs.gfx.particles.Pool.record"></a>
#### tecs.gfx.particles.Pool:record <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

```teal
function tecs.gfx.particles.Pool.record(
    self, frame: Frame, instances: Buffer, bounds: Buffer
)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Pool` |  |
| `frame` | `Frame` |  |
| `instances` | `Buffer` |  |
| `bounds` | `Buffer` |  |

##### Returns

None.

<a id="tecs.gfx.particles.Pool.takeDirty"></a>
#### tecs.gfx.particles.Pool:takeDirty <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

```teal
function tecs.gfx.particles.Pool.takeDirty(self): {integer}
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Pool` |  |

##### Returns

| Type | Description |
| --- | --- |
| `{integer}` |  |

<a id="tecs.gfx.particles.Pool.write"></a>
#### tecs.gfx.particles.Pool:write <span class="tealdoc-kind-badge tealdoc-kind-instance">Instance</span>

Hides the run and remembers where it landed.

Only ever called on a frame the layout moved, since nothing else is ever
dirty. What it writes is the hidden slot every reserved run writes, and the
simulate pass overwrites the live ones later on the same frame: the flush is
recorded before the dispatch, so a relayout cannot lose a particle.


```teal
function tecs.gfx.particles.Pool.write(
    self,
    floats: loader.CArray,
    bounds: loader.CArray,
    base: integer,
    first: integer,
    last: integer
)
```

##### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `self` | `Pool` |  |
| `floats` | `loader.CArray` |  |
| `bounds` | `loader.CArray` |  |
| `base` | `integer` |  |
| `first` | `integer` |  |
| `last` | `integer` |  |

##### Returns

None.

<a id="tecs.gfx.particles.PoolOptions"></a>
### tecs.gfx.particles.PoolOptions <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Configures `plugin`.


```teal
record tecs.gfx.particles.PoolOptions
    renderer: Renderer
    capacity: integer
    maxEmitters: integer
end
```

<a id="tecs.gfx.particles.PoolOptions.renderer"></a>
#### tecs.gfx.particles.PoolOptions.renderer <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects the renderer whose instance buffer the pool uses
and whose device its buffers and pipelines are built on. Required, and an
absent one raises.


```teal
tecs.gfx.particles.PoolOptions.renderer: Renderer
```

<a id="tecs.gfx.particles.PoolOptions.capacity"></a>
#### tecs.gfx.particles.PoolOptions.capacity <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets how many live particles the pool holds across the
world. Defaults to 16384. The value remains fixed at install: changing it
would move the run, and moving the run lays out the whole scene again.


```teal
tecs.gfx.particles.PoolOptions.capacity: integer
```

<a id="tecs.gfx.particles.PoolOptions.maxEmitters"></a>
#### tecs.gfx.particles.PoolOptions.maxEmitters <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets how many emitters the pool holds at once. Defaults
to 256. An emitter that arrives past the limit draws nothing and logs the
refusal.


```teal
tecs.gfx.particles.PoolOptions.maxEmitters: integer
```

<a id="tecs.gfx.particles.Range"></a>
### tecs.gfx.particles.Range <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

The two-bound form of a `Value`.


```teal
record tecs.gfx.particles.Range
    min: number
    max: number
end
```

<a id="tecs.gfx.particles.Range.min"></a>
#### tecs.gfx.particles.Range.min <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the inclusive lower bound.


```teal
tecs.gfx.particles.Range.min: number
```

<a id="tecs.gfx.particles.Range.max"></a>
#### tecs.gfx.particles.Range.max <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the inclusive upper bound.


```teal
tecs.gfx.particles.Range.max: number
```

<a id="tecs.gfx.particles.RenderOptions"></a>
### tecs.gfx.particles.RenderOptions <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Configures how particles draw.


```teal
global record tecs.gfx.particles.RenderOptions
    material: string
    materialParam: number
    sprite: Sprite
    sheet: Sheet
    tag: string
    layer: integer
    pivotX: number
    pivotY: number
    alignment: string
    stretch: number
    blend: string
    clip: integer
end
```

<a id="tecs.gfx.particles.RenderOptions.material"></a>
#### tecs.gfx.particles.RenderOptions.material <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects the material that shades the quad by name. An
absent value samples the image array and covers the whole quad.


```teal
tecs.gfx.particles.RenderOptions.material: string
```

<a id="tecs.gfx.particles.RenderOptions.materialParam"></a>
#### tecs.gfx.particles.RenderOptions.materialParam <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Passes one number to the material, clamped to zero
through `0.999` and ignored unless `material` names one. The instance
carries the material as `id + param` in one float, and the vertex shader
reads the integer part as the selector and the fraction as the
parameter, so `1.0` would arrive as the next material's id with a
parameter of zero. Defaults to zero.


```teal
tecs.gfx.particles.RenderOptions.materialParam: number
```

<a id="tecs.gfx.particles.RenderOptions.sprite"></a>
#### tecs.gfx.particles.RenderOptions.sprite <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects a registered image from `renderer.sprites:sprite`. An
absent value draws a white quad.


```teal
tecs.gfx.particles.RenderOptions.sprite: Sprite
```

<a id="tecs.gfx.particles.RenderOptions.sheet"></a>
#### tecs.gfx.particles.RenderOptions.sheet <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects a sheet to animate instead of a still region.
The cycle is played exactly once over each particle's own life and clamps
at the end, so a randomized lifetime randomizes the playback speed.

Uses the same frame table an animated entity does, so per-frame
durations, reverse and pingpong all arrive already spent.


```teal
tecs.gfx.particles.RenderOptions.sheet: Sheet
```

<a id="tecs.gfx.particles.RenderOptions.tag"></a>
#### tecs.gfx.particles.RenderOptions.tag <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects the sheet tag to animate. An absent value
animates the whole sheet. A name the sheet does not carry animates the
whole sheet too, and the sheet reports it at error level under the
`tecs.gfx` logger, naming the tags it does carry. Defining the effect
still succeeds, so a sheet re-exported without a tag keeps drawing.


```teal
tecs.gfx.particles.RenderOptions.tag: string
```

<a id="tecs.gfx.particles.RenderOptions.layer"></a>
#### tecs.gfx.particles.RenderOptions.layer <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects the render layer from one to `layers.MAX`.
Defaults to one, and anything outside the range raises.


```teal
tecs.gfx.particles.RenderOptions.layer: integer
```

<a id="tecs.gfx.particles.RenderOptions.pivotX"></a>
#### tecs.gfx.particles.RenderOptions.pivotX <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the horizontal pivot as a fraction of the
frame from its top left. Defaults to `0.5`, the middle.


```teal
tecs.gfx.particles.RenderOptions.pivotX: number
```

<a id="tecs.gfx.particles.RenderOptions.pivotY"></a>
#### tecs.gfx.particles.RenderOptions.pivotY <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the vertical pivot as a fraction of the frame
from its top edge. Defaults to `0.5`, the middle.


```teal
tecs.gfx.particles.RenderOptions.pivotY: number
```

<a id="tecs.gfx.particles.RenderOptions.alignment"></a>
#### tecs.gfx.particles.RenderOptions.alignment <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects `"fixed"` or `"velocity"` alignment. Velocity
alignment adds the path's angle to the particle's own rotation, so a
spinning spark aligned to its path still spins. Defaults to `"fixed"`,
and anything else raises.


```teal
tecs.gfx.particles.RenderOptions.alignment: string
```

<a id="tecs.gfx.particles.RenderOptions.stretch"></a>
#### tecs.gfx.particles.RenderOptions.stretch <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Multiplies the along-path axis when aligned to
velocity. Defaults to one.


```teal
tecs.gfx.particles.RenderOptions.stretch: number
```

<a id="tecs.gfx.particles.RenderOptions.blend"></a>
#### tecs.gfx.particles.RenderOptions.blend <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects `"alpha"`, `"additive"`, or `"opaque"`. Defaults
to `"alpha"`, and anything else raises.

`"alpha"` and `"additive"` draw in the forward pass over the composited
image, depth tested and not written, so a particle is still hidden by
geometry in front of it. `"alpha"` keeps that much less of what is behind
it and `"additive"` adds to it, so a dark additive particle changes
nothing and overlapping ones brighten.

`"opaque"` draws into the G-buffer instead. What it gives up is alpha,
which the G-buffer is written with replace and has nowhere to put: a fade
to transparent writes opaque over what was behind it, and the only fade
left is a size curve reaching zero. What it buys is everything that reads
the G-buffer, which is the occluder mask and being shadowed by one.


```teal
tecs.gfx.particles.RenderOptions.blend: string
```

<a id="tecs.gfx.particles.RenderOptions.clip"></a>
#### tecs.gfx.particles.RenderOptions.clip <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects the clip region that contains the fragments.
Defaults to zero, which is no clipping.


```teal
tecs.gfx.particles.RenderOptions.clip: integer
```

<a id="tecs.gfx.particles.ScheduleOptions"></a>
### tecs.gfx.particles.ScheduleOptions <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Configures when particles emit.


```teal
global record tecs.gfx.particles.ScheduleOptions
    rate: number
    duration: number
    looping: boolean
    delay: number
    bursts: {ScheduleBurst}
end
```

<a id="tecs.gfx.particles.ScheduleOptions.rate"></a>
#### tecs.gfx.particles.ScheduleOptions.rate <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the continuous emission rate in particles per
second. Defaults to zero, which emits only what `bursts` asks for.


```teal
tecs.gfx.particles.ScheduleOptions.rate: number
```

<a id="tecs.gfx.particles.ScheduleOptions.duration"></a>
#### tecs.gfx.particles.ScheduleOptions.duration <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the emission cycle duration in seconds. Defaults to
zero, which leaves the cycle open.


```teal
tecs.gfx.particles.ScheduleOptions.duration: number
```

<a id="tecs.gfx.particles.ScheduleOptions.looping"></a>
#### tecs.gfx.particles.ScheduleOptions.looping <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Controls whether the emission cycle repeats. Defaults to
false.


```teal
tecs.gfx.particles.ScheduleOptions.looping: boolean
```

<a id="tecs.gfx.particles.ScheduleOptions.delay"></a>
#### tecs.gfx.particles.ScheduleOptions.delay <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the delay before emission starts in seconds.
Defaults to zero.


```teal
tecs.gfx.particles.ScheduleOptions.delay: number
```

<a id="tecs.gfx.particles.ScheduleOptions.bursts"></a>
#### tecs.gfx.particles.ScheduleOptions.bursts <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Defines at most four timed burst counts measured from
the start of the cycle. A muzzle flash is one burst at time zero.
Defaults to none, and a fifth raises.


```teal
tecs.gfx.particles.ScheduleOptions.bursts: {ScheduleBurst}
```

<a id="tecs.gfx.particles.SpawnOptions"></a>
### tecs.gfx.particles.SpawnOptions <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Configures where particles spawn.


```teal
global record tecs.gfx.particles.SpawnOptions
    shape: string
    width: number
    height: number
    arc: number
    rotation: number
    distribution: string
    direction: number
    spread: number
    outward: boolean
    space: string
    inheritVelocity: number
end
```

<a id="tecs.gfx.particles.SpawnOptions.shape"></a>
#### tecs.gfx.particles.SpawnOptions.shape <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects `"point"`, `"line"`, `"rectangle"`,
`"rectangleEdge"`, `"disc"`, `"ring"`, or `"cone"`. Defaults to
`"point"`, and anything else raises.


```teal
tecs.gfx.particles.SpawnOptions.shape: string
```

<a id="tecs.gfx.particles.SpawnOptions.width"></a>
#### tecs.gfx.particles.SpawnOptions.width <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets width for a line or rectangle and radius for a
disc, ring, or cone. Defaults to zero.


```teal
tecs.gfx.particles.SpawnOptions.width: number
```

<a id="tecs.gfx.particles.SpawnOptions.height"></a>
#### tecs.gfx.particles.SpawnOptions.height <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets height for a rectangle. Every other shape ignores
it. Defaults to zero.


```teal
tecs.gfx.particles.SpawnOptions.height: number
```

<a id="tecs.gfx.particles.SpawnOptions.arc"></a>
#### tecs.gfx.particles.SpawnOptions.arc <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the sampled sector of a disc, ring, or cone in
radians. Defaults to a full turn.


```teal
tecs.gfx.particles.SpawnOptions.arc: number
```

<a id="tecs.gfx.particles.SpawnOptions.rotation"></a>
#### tecs.gfx.particles.SpawnOptions.rotation <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Rotates the sampled area in radians, independently of
launch direction. Defaults to zero.


```teal
tecs.gfx.particles.SpawnOptions.rotation: number
```

<a id="tecs.gfx.particles.SpawnOptions.distribution"></a>
#### tecs.gfx.particles.SpawnOptions.distribution <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects `"uniform"` or `"normal"` sampling. A gaussian
spread makes a column read as a column rather than as a slab. Defaults to
`"uniform"`, and anything else raises.


```teal
tecs.gfx.particles.SpawnOptions.distribution: string
```

<a id="tecs.gfx.particles.SpawnOptions.direction"></a>
#### tecs.gfx.particles.SpawnOptions.direction <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the launch direction in radians and centers the
full cone on it, so a particle leaves within plus or minus half of
`spread`. Half-angle is the other plausible reading of `spread` and a
mismatch is silent, so it is said here. Defaults to zero.


```teal
tecs.gfx.particles.SpawnOptions.direction: number
```

<a id="tecs.gfx.particles.SpawnOptions.spread"></a>
#### tecs.gfx.particles.SpawnOptions.spread <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the full cone width in radians. Defaults to zero,
which launches every particle along `direction`.


```teal
tecs.gfx.particles.SpawnOptions.spread: number
```

<a id="tecs.gfx.particles.SpawnOptions.outward"></a>
#### tecs.gfx.particles.SpawnOptions.outward <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Launches along the shape's outward normal rather than
along `direction`. Meaningful for the shapes that have one: disc, ring,
cone and a rectangle's edge. Defaults to false.


```teal
tecs.gfx.particles.SpawnOptions.outward: boolean
```

<a id="tecs.gfx.particles.SpawnOptions.space"></a>
#### tecs.gfx.particles.SpawnOptions.space <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Selects `"local"` or `"world"` space. Local particles
follow the emitter's transform for their whole life; world particles read
it once, at spawn, and then move independently. Neither is a good
universal default, so it is explicit and defaults to `"world"`. Anything
else raises.


```teal
tecs.gfx.particles.SpawnOptions.space: string
```

<a id="tecs.gfx.particles.SpawnOptions.inheritVelocity"></a>
#### tecs.gfx.particles.SpawnOptions.inheritVelocity <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets how much emitter velocity a particle inherits, from
zero to one. Defaults to zero, and world space is the only space it means
anything in: a local particle is already carried by the emitter, so
inheriting would carry it twice.


```teal
tecs.gfx.particles.SpawnOptions.inheritVelocity: number
```

<a id="tecs.gfx.particles.UpdateOptions"></a>
### tecs.gfx.particles.UpdateOptions <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

Configures how a particle changes over its normalized lifetime.


```teal
global record tecs.gfx.particles.UpdateOptions
    drag: number
    radialAcceleration: Value
    tangentialAcceleration: Value
    size: Curve
    color: Gradient
end
```

<a id="tecs.gfx.particles.UpdateOptions.drag"></a>
#### tecs.gfx.particles.UpdateOptions.drag <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets damping in units of one over seconds, applied as
`velocity = velocity / (1 + drag * dt)`, so it composes with any step.
Defaults to zero.


```teal
tecs.gfx.particles.UpdateOptions.drag: number
```

<a id="tecs.gfx.particles.UpdateOptions.radialAcceleration"></a>
#### tecs.gfx.particles.UpdateOptions.radialAcceleration <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets acceleration away from the emitter. This and
`tangentialAcceleration` are the difference between a fountain and a
vortex. Defaults to zero.


```teal
tecs.gfx.particles.UpdateOptions.radialAcceleration: Value
```

<a id="tecs.gfx.particles.UpdateOptions.tangentialAcceleration"></a>
#### tecs.gfx.particles.UpdateOptions.tangentialAcceleration <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets acceleration at right angles to the emitter.
Defaults to zero.


```teal
tecs.gfx.particles.UpdateOptions.tangentialAcceleration: Value
```

<a id="tecs.gfx.particles.UpdateOptions.size"></a>
#### tecs.gfx.particles.UpdateOptions.size <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Multiplies each particle's size over its life. An absent
curve multiplies by one, so leaving it out costs nothing.


```teal
tecs.gfx.particles.UpdateOptions.size: Curve
```

<a id="tecs.gfx.particles.UpdateOptions.color"></a>
#### tecs.gfx.particles.UpdateOptions.color <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Multiplies each particle's color over its life. An absent
gradient multiplies by white, so leaving it out costs nothing. Its alpha
fades a blended effect and is ignored by an effect whose `render.blend` is
`"opaque"`.


```teal
tecs.gfx.particles.UpdateOptions.color: Gradient
```

<a id="tecs.gfx.particles.Value"></a>
### tecs.gfx.particles.Value <span class="tealdoc-kind-badge tealdoc-kind-type">type</span>

Accepted wherever a property may vary per particle.


```teal
type tecs.gfx.particles.Value = Value
```

## Functions

<a id="tecs.gfx.particles.find"></a>
### tecs.gfx.particles.find <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the effect registered under `name`, or nil when none exists.



```teal
function tecs.gfx.particles.find(name: string): Effect
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `name` | `string` | Nil answers nil rather than raising, so a restore holding no name takes the same path as one holding an unknown name. |

#### Returns

| Type | Description |
| --- | --- |
| [`Effect`](/modules/gfx/particles/#tecs.gfx.particles.Effect) | The registered effect itself, not a copy, so two callers finding one name share it. Nil when nothing has that name. |

<a id="tecs.gfx.particles.names"></a>
### tecs.gfx.particles.names <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns every registered effect name in registration order.



```teal
function tecs.gfx.particles.names(): {string}
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `{string}` | A fresh table each call, the caller's to keep and to modify. |

<a id="tecs.gfx.particles.plugin"></a>
### tecs.gfx.particles.plugin <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Installs the pool on a world. Without it, Tecs allocates and dispatches
no particle work.



```teal
function tecs.gfx.particles.plugin(options: PoolOptions): function(
    World
)
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `options` | [`PoolOptions`](/modules/gfx/particles/#tecs.gfx.particles.PoolOptions) | Requires `renderer`; both capacities have defaults. |

#### Returns

| Type | Description |
| --- | --- |
| `function(`[`World`](/modules/ecs/#tecs.World)`)` | Returns the plugin to install on a world. Installing it on a world that already has a pool does nothing, and installing it on two worlds gives each its own run of the one renderer's buffer. |

<a id="tecs.gfx.particles.poolOf"></a>
### tecs.gfx.particles.poolOf <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the pool installed on a world, or nil. Tests and tooling use
this function.



```teal
function tecs.gfx.particles.poolOf(world: World): Pool
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `world` | [`World`](/modules/ecs/#tecs.World) | Accepts a world with or without the plugin. |

#### Returns

| Type | Description |
| --- | --- |
| [`Pool`](/modules/gfx/particles/#tecs.gfx.particles.Pool) | Returns the world's live pool, or nil when the plugin was never installed. The function does not copy the pool. |

<a id="tecs.gfx.particles.reset"></a>
### tecs.gfx.particles.reset <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Forgets every registered effect, so a spec can register a set again.
Every handle already handed out goes stale with it.


```teal
function tecs.gfx.particles.reset()
```

#### Arguments

None.

#### Returns

None.

## Values

<a id="tecs.gfx.particles.CURVE_SAMPLES"></a>
### tecs.gfx.particles.CURVE_SAMPLES <span class="tealdoc-kind-badge tealdoc-kind-variable">variable</span>

Read-only. Reports how many samples one compiled curve or gradient
holds.


```teal
tecs.gfx.particles.CURVE_SAMPLES: integer
```

<a id="tecs.gfx.particles.EFFECT"></a>
### tecs.gfx.particles.EFFECT <span class="tealdoc-kind-badge tealdoc-kind-variable">variable</span>

Read-only. Reports each field's offset within an effect record.

Published because the shaders state the same offsets and a
disagreement between the two is silent: it draws something, just not
the right thing. `spec/particles_spec.lua` reads both and holds them to
each other, which is the only reason this is on the surface.


```teal
tecs.gfx.particles.EFFECT: {string: integer}
```

<a id="tecs.gfx.particles.EFFECT_FLOATS"></a>
### tecs.gfx.particles.EFFECT_FLOATS <span class="tealdoc-kind-badge tealdoc-kind-variable">variable</span>

Read-only. Reports how many floats each effect record contains.


```teal
tecs.gfx.particles.EFFECT_FLOATS: integer
```

<a id="tecs.gfx.particles.EMITTER"></a>
### tecs.gfx.particles.EMITTER <span class="tealdoc-kind-badge tealdoc-kind-variable">variable</span>

Read-only. Reports each field's float offset within an emitter record,
counting from zero.


```teal
tecs.gfx.particles.EMITTER: {string: integer}
```

<a id="tecs.gfx.particles.EMITTER_FLOATS"></a>
### tecs.gfx.particles.EMITTER_FLOATS <span class="tealdoc-kind-badge tealdoc-kind-variable">variable</span>

Read-only. Reports how many floats each emitter record contains. An
emitter record is
much the smaller of the two.


```teal
tecs.gfx.particles.EMITTER_FLOATS: integer
```

<a id="tecs.gfx.particles.STATE_FLOATS"></a>
### tecs.gfx.particles.STATE_FLOATS <span class="tealdoc-kind-badge tealdoc-kind-variable">variable</span>

Read-only. Reports how many GPU state floats one particle carries.


```teal
tecs.gfx.particles.STATE_FLOATS: integer
```