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

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.

InputTypeDescription
paramSprites.Reactive.ValueA 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);
A float parameter reads its number; a vec2 parameter reads its { 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.

InputTypeDescription
paramSprites.Reactive.ValueA 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);
The parameter's name and its float value become one descriptor the driver writes as a uniform.

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.

InputTypeDescription
dataSprites.Reactive.ValueThe document, whose params field holds the image parameters.
layerSprites.Reactive.ValueThe 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);
  });
});
The image parameter comes first, the layer parameter after, each as a name and value descriptor.

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.

InputTypeDescription
dataSprites.Reactive.ValueThe document, read for the layer's source and uniforms.
layersSprites.Reactive.ValueThe layer array the layer at index is taken from.
widthSprites.Reactive.ValueThe render width in pixels.
heightSprites.Reactive.ValueThe render height in pixels.
indexSprites.Reactive.ValueThe layer's position in the array.
belowSprites.Reactive.ValueThe 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);
The starter layer renders to a handle the surface draws. It needs WebGL2 to show the image.

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.

InputTypeDescription
dataSprites.Reactive.ValueThe document, read for each layer's source and uniforms.
layersSprites.Reactive.ValueThe layer array the chain walks.
widthSprites.Reactive.ValueThe render width in pixels.
heightSprites.Reactive.ValueThe render height in pixels.
countSprites.Reactive.ValueThe number of layers, where the recursion stops.
indexNumberThe layer to render at this step, starting at 0.
belowSprites.Reactive.ValueThe 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);
The chain emits each layer's composite; the top one is the final image. It needs WebGL2 to show it.

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.

InputTypeDescription
handleSprites.Reactive.ValueA 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);
A handle with an error reads its message; an absent handle reads no error at all.

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.

InputTypeDescription
handlesSprites.Reactive.ValueThe 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);
The first layer compiles, so the first error is the second layer's message.

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.

InputTypeDescription
errorSprites.Reactive.ValueThe 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);
});
The panel overlays the stage with the compile error while there is one.

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.

InputTypeDescription
dataSprites.Reactive.ValueThe 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');
The starter document's chain reports its render size and one composite 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.

InputTypeDescription
frameSprites.Reactive.ValueThe render chain frame from Chain.
thumbSprites.Reactive.ValueThe store field the saver writes a preview of the rendered image into.
infoSprites.Reactive.ValueThe store field the saver writes the info summary into.
summarySprites.Reactive.ValueThe reactive info text: the size and layer count.
panActiveSprites.Reactive.ValueThe flag that gates the drag pan.
scaleSprites.Reactive.ValueThe view scale the wheel, drag and Reset drive.
offsetSprites.Reactive.ValueThe 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);
});
The stage draws the checker ground and the composite over it. It needs WebGL2 to show the image.