Sprites.Media.Shader.Canvas
The shader editor's stage. It folds the visible layers through the Sprites.Shader driver into a chain of passes: each layer renders into its own reused buffer, taking the one below as its uInput, bottom to top, so the layers compose like a chain of effects. It draws the result to a canvas that pans and zooms under the Move tool, shows compile errors over the stage, saves the thumbnail and info summary a little after the image settles, and shows per-layer previews. Open the running editor at image/shader/ to see the stage in use.
- Sprites.Media.Shader.Canvas.ParamValue
- Sprites.Media.Shader.Canvas.Descriptor
- Sprites.Media.Shader.Canvas.Uniforms
- Sprites.Media.Shader.Canvas.Rendered
- Sprites.Media.Shader.Canvas.From
- Sprites.Media.Shader.Canvas.HandleError
- Sprites.Media.Shader.Canvas.FirstError
- Sprites.Media.Shader.Canvas.ErrorPanel
- Sprites.Media.Shader.Canvas.Chain
- Sprites.Media.Shader.Canvas.View
Sprites.Media.Shader.Canvas.ParamValue Sprites.Reactive.Value
Sprites.Media.Shader.Canvas.ParamValue(param)
The uniform value a parameter carries, chosen by its type: the float for a float, or the vector object for a vec2, vec3, vec4 or colour, with a zero of the right shape when the value is missing. It selects by the type field so the shader gets the value the type declares.
| Input | Type | Description |
|---|---|---|
param | Sprites.Reactive.Value | A parameter with a type and a tagged-union value. |
Returns the reactive uniform value the type declares: a number or a vector object.
const floatParam = Sprites.Reactive.Value({ type: 'float', value: { optionalFloat: 0.75 } });
const vec2Param = Sprites.Reactive.Value({ type: 'vec2', value: { optionalVec2: { x: 3, y: 4 } } });
const asFloat = Sprites.Media.Shader.Canvas.ParamValue(floatParam);
const asVec2 = Sprites.Media.Shader.Canvas.ParamValue(vec2Param);
const x = Sprites.Object.Field(asVec2, 'x');
const y = Sprites.Object.Field(asVec2, 'y');
Sprites.Ui.Dom.Text('float: ', asFloat, ' | vec2: ', x, ', ', y);
{ x, y } vector.Sprites.Media.Shader.Canvas.Descriptor Sprites.Reactive.Value
Sprites.Media.Shader.Canvas.Descriptor(param)
One uniform descriptor for a parameter: its name paired with its value. It reads the parameter's name, reads its value through ParamValue so the value matches the type the parameter declares, and reads both into a { name, value } object the render driver writes as a uniform.
| Input | Type | Description |
|---|---|---|
param | Sprites.Reactive.Value | A parameter with a name, a type and a tagged-union value. |
Returns a reactive { name, value } descriptor.
const param = Sprites.Reactive.Value({ name: 'uAmount', type: 'float', value: { optionalFloat: 0.5 } });
const descriptor = Sprites.Media.Shader.Canvas.Descriptor(param);
const name = Sprites.Object.Field(descriptor, 'name');
const value = Sprites.Object.Field(descriptor, 'value');
Sprites.Ui.Dom.Text(name, ' = ', value);
Sprites.Media.Shader.Canvas.Uniforms Sprites.Reactive.Value
Sprites.Media.Shader.Canvas.Uniforms(data, layer)
The uniform list for a layer: the image parameters then the layer's own parameters, each a { name, value } descriptor, gathered into one reactive array. A layer parameter of the same name comes after the image one, so the driver's later write shadows it. It follows both parameter lists, so an added or edited parameter flows straight into the array.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The document, whose params field holds the image parameters. |
layer | Sprites.Reactive.Value | The layer, whose params field holds its own parameters. |
Returns a reactive array of { name, value } descriptors.
const data = Sprites.Reactive.Value({ params: [{ name: 'uScale', type: 'float', value: { optionalFloat: 2 } }] });
const layer = Sprites.Reactive.Value({ params: [{ name: 'uTint', type: 'float', value: { optionalFloat: 1 } }] });
const uniforms = Sprites.Media.Shader.Canvas.Uniforms(data, layer);
Sprites.Reactive.Each(uniforms, (descriptor) => {
const name = Sprites.Object.Field(descriptor, 'name');
const value = Sprites.Object.Field(descriptor, 'value');
Sprites.Ui.Dom.Tag('p', () => {
Sprites.Ui.Dom.Text(name, ' = ', value);
});
});
Sprites.Media.Shader.Canvas.Rendered Sprites.Reactive.Value
Sprites.Media.Shader.Canvas.Rendered(data, layers, width, height, index, below)
The composite handle for one layer: its render when visible, or the layer below unchanged when hidden. It gathers an If and an Else over the layer's visible flag, each branch emitting its value, and fetches the one that mounted. The render lives inside the visible branch, so hiding the layer unmounts it and frees its buffer through the driver, and shows the layer below straight through.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The document, read for the layer's source and uniforms. |
layers | Sprites.Reactive.Value | The layer array the layer at index is taken from. |
width | Sprites.Reactive.Value | The render width in pixels. |
height | Sprites.Reactive.Value | The render height in pixels. |
index | Sprites.Reactive.Value | The layer's position in the array. |
below | Sprites.Reactive.Value | The composite handle of the layers underneath, threaded in as uInput. |
Returns a reactive handle: the layer's composite, or the one below when it is hidden.
const data = Sprites.Media.Shader.NewDocument(128, 128);
const layers = Sprites.Object.Field(data, 'layers');
const width = Sprites.Object.Field(data, 'w');
const height = Sprites.Object.Field(data, 'h');
const handle = Sprites.Media.Shader.Canvas.Rendered(data, layers, width, height, 0, undefined);
const scale = Sprites.Reactive.Value(1);
const offset = Sprites.Reactive.Value({ x: 0, y: 0 });
const pan = Sprites.Reactive.Value(false);
Sprites.Shader.Surface(handle, scale, offset, pan);
Sprites.Media.Shader.Canvas.From Void
Sprites.Media.Shader.Canvas.From(data, layers, width, height, count, index, below)
The chain from a layer index upward, given below, the composite of the layers underneath. An If over whether a layer sits at the index renders this layer, emits its composite for the previews, and recurses to the next layer threading this layer's composite as the new below. Past the top layer the If does not build, so the recursion is finite and re-expands or contracts as layers are added or removed. It emits into the previews gather and returns nothing.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The document, read for each layer's source and uniforms. |
layers | Sprites.Reactive.Value | The layer array the chain walks. |
width | Sprites.Reactive.Value | The render width in pixels. |
height | Sprites.Reactive.Value | The render height in pixels. |
count | Sprites.Reactive.Value | The number of layers, where the recursion stops. |
index | Number | The layer to render at this step, starting at 0. |
below | Sprites.Reactive.Value | The composite handle underneath, undefined at the bottom. |
Returns nothing. It emits each layer's composite into the surrounding previews gather.
const data = Sprites.Media.Shader.NewDocument(128, 128);
const layers = Sprites.Object.Field(data, 'layers');
const width = Sprites.Object.Field(data, 'w');
const height = Sprites.Object.Field(data, 'h');
const count = Sprites.Array.Length(layers);
const last = Sprites.Maths.Subtract(count, 1);
const previews = Sprites.Array.Gather(() => {
Sprites.Media.Shader.Canvas.From(data, layers, width, height, count, 0, undefined);
});
const composite = Sprites.Array.At(previews, last);
const scale = Sprites.Reactive.Value(1);
const offset = Sprites.Reactive.Value({ x: 0, y: 0 });
const pan = Sprites.Reactive.Value(false);
Sprites.Shader.Surface(composite, scale, offset, pan);
Sprites.Media.Shader.Canvas.HandleError Sprites.Reactive.Value
Sprites.Media.Shader.Canvas.HandleError(handle)
The compile error a handle carries, or undefined: its optionalError field, read through a safe stand-in when the handle is absent, so a hidden or unrendered layer reads no error.
| Input | Type | Description |
|---|---|---|
handle | Sprites.Reactive.Value | A render handle with an optionalError field, or undefined. |
Returns a reactive error string, or undefined when the handle has no error.
const handle = Sprites.Reactive.Value({ optionalError: 'main: undeclared identifier' });
const error = Sprites.Media.Shader.Canvas.HandleError(handle);
const clean = Sprites.Media.Shader.Canvas.HandleError(undefined);
const shown = Sprites.Optional.Default(error, '(none)');
const safe = Sprites.Optional.Default(clean, '(none)');
Sprites.Ui.Dom.Text('handle: ', shown, ' | absent: ', safe);
Sprites.Media.Shader.Canvas.FirstError Sprites.Reactive.Value
Sprites.Media.Shader.Canvas.FirstError(handles)
The first compile error across the per-layer handles, or undefined when every layer compiles. It maps each handle to its error through HandleError, finds the first present one, and reads it, giving undefined past the end when there is none.
| Input | Type | Description |
|---|---|---|
handles | Sprites.Reactive.Value | The per-layer composite handles, each carrying an optional error. |
Returns a reactive error string: the first layer's error, or undefined when all compile.
const handles = Sprites.Reactive.Value([
{ optionalError: undefined },
{ optionalError: 'layer 2 failed to compile' }
]);
const error = Sprites.Media.Shader.Canvas.FirstError(handles);
const shown = Sprites.Optional.Default(error, 'all layers compile');
Sprites.Ui.Dom.Text('first error: ', shown);
Sprites.Media.Shader.Canvas.ErrorPanel Void
Sprites.Media.Shader.Canvas.ErrorPanel(error)
The compile-error overlay: a panel over the stage showing the first layer's error, while there is one. error is the reactive error string.
| Input | Type | Description |
|---|---|---|
error | Sprites.Reactive.Value | The reactive error string shown in the panel. |
Returns nothing.
const error = Sprites.Reactive.Binding'ERROR: 0:12 syntax error, unexpected identifier');
Sprites.Ui.Dom.Tag('div', () => {
Sprites.Ui.Dom.Style('position', 'relative');
Sprites.Ui.Dom.Style('height', '120px');
Sprites.Ui.Dom.Style('background', '#20302a');
Sprites.Media.Shader.Canvas.ErrorPanel(error);
});
Sprites.Media.Shader.Canvas.Chain Sprites.Reactive.Value
Sprites.Media.Shader.Canvas.Chain(data)
The render chain as a reactive frame: the final composited handle, the per-layer composite handles for the previews, the size and the first compile error. The recursion drives the chain and fans the per-layer composites into a previews gather; the top layer's composite is the final image. It reads into a { composite, previews, width, height, optionalError } frame.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The document, whose w, h and layers fields drive the chain. |
Returns a reactive { composite, previews, width, height, optionalError } frame.
const data = Sprites.Media.Shader.NewDocument(128, 128);
const frame = Sprites.Media.Shader.Canvas.Chain(data);
const width = Sprites.Object.Field(frame, 'width');
const height = Sprites.Object.Field(frame, 'height');
const previews = Sprites.Object.Field(frame, 'previews');
const passes = Sprites.Array.Length(previews);
Sprites.Ui.Dom.Text('size ', width, ' x ', height, ', ', passes, ' pass');
Sprites.Media.Shader.Canvas.View Void
Sprites.Media.Shader.Canvas.View(frame, thumb, info, summary, panActive, scale, offset)
The stage: a checker ground filling the editor stage, the render surface centred over it, and the compile-error overlay, with the thumbnail saver following the composite. It draws the frame's composite through the render surface, shows the first compile error over it while there is one, and writes the thumbnail and info summary a little after the image settles.
| Input | Type | Description |
|---|---|---|
frame | Sprites.Reactive.Value | The render chain frame from Chain. |
thumb | Sprites.Reactive.Value | The store field the saver writes a preview of the rendered image into. |
info | Sprites.Reactive.Value | The store field the saver writes the info summary into. |
summary | Sprites.Reactive.Value | The reactive info text: the size and layer count. |
panActive | Sprites.Reactive.Value | The flag that gates the drag pan. |
scale | Sprites.Reactive.Value | The view scale the wheel, drag and Reset drive. |
offset | Sprites.Reactive.Value | The view { x, y } offset the wheel, drag and Reset drive. |
Returns nothing.
const data = Sprites.Media.Shader.NewDocument(128, 128);
const width = Sprites.Object.Field(data, 'w');
const height = Sprites.Object.Field(data, 'h');
const layers = Sprites.Object.Field(data, 'layers');
const frame = Sprites.Media.Shader.Canvas.Chain(data);
const summary = Sprites.Media.Shader.Summary(width, height, layers);
const thumb = Sprites.Reactive.Value(undefined);
const info = Sprites.Reactive.Value(undefined);
const pan = Sprites.Reactive.Value(false);
const scale = Sprites.Reactive.Value(1);
const offset = Sprites.Reactive.Value({ x: 0, y: 0 });
Sprites.Ui.Dom.Tag('div', () => {
Sprites.Ui.Dom.Style('position', 'relative');
Sprites.Ui.Dom.Style('height', '180px');
Sprites.Media.Shader.Canvas.View(frame, thumb, info, summary, pan, scale, offset);
});