Write Your First 360 Shader
A complete native GLSL shader, a parameter, content-time animation and focused Shader Workshop troubleshooting
Last updated: 2026-10-17
This companion starts with complete native direction_360 source. The 360 Shaders Workshop is Alpha; the expected colors below describe what this code computes, not a claim of observed GPU output. See the Workshop guide for opening, saving, checking and importing a Shader, the .hexshader format reference for implementers, and the 360 Shaders feature overview.
These original examples and this tutorial are dedicated to the public domain under CC0 1.0. Your own shader files and imported resources can carry other terms.
First source: a directional sky
In +CREATE → 360 Shaders → Enter Shader Workshop, create a native shader and replace its root source.glsl with this entire file. Keep the JSON header: it declares the source schema, ABI, sphere space and one typed control. The Workshop host supplies HxFrag, hxParam_gain and main().
/*@hextile-shader
{
"schema": 1,
"abi": "hx-shader/1",
"space": "direction_360",
"parameters": [
{"id": "gain", "type": "float", "default": 0.8,
"label": "Brightness", "ui": {"control": "slider", "min": 0.0, "max": 1.0, "step": 0.01}}
]
}
*/
vec4 hxMain(in HxFrag hx) {
float up = clamp(0.5 + 0.5 * hx.direction.y, 0.0, 1.0);
vec3 horizon = vec3(0.08, 0.18, 0.32);
vec3 zenith = vec3(0.35, 0.55, 0.90);
vec3 sky = mix(horizon, zenith, up) * hxParam_gain;
return vec4(sky, 1.0);
}
Use Check & Preview before accepting the source. The expected design is a darker lower hemisphere and brighter blue toward +Y. Moving around the sphere changes which direction is sampled. Adjust Brightness and save a Named Look if you want to retain that value. The parameter's ID, type and default are declared in the header; current values and Looks are separate from source. Native output values are linear sRGB with straight alpha. Hextile's PNG output composites its background and writes opaque sRGB.
hx.direction is a unit direction with +Y up and +Z forward. hx.uv rises from south to north; hx.mapUv descends from north to south. hx.fragCoord and hx.resolution describe the full output, not a tile. The native entry must be vec4 hxMain(in HxFrag hx); a Shadertoy mainImage belongs to a different declared space. The host supplies the capture uniforms and main().
Animate with content time
To make a pulse, replace the entire root file with this timed variant. It uses the same typed control and adds hx.time, the sampled content time supplied by the host. The expected design is the same blue gradient gently changing brightness; it is an instructional expectation, not a recorded render.
/*@hextile-shader
{
"schema": 1,
"abi": "hx-shader/1",
"space": "direction_360",
"parameters": [
{"id": "gain", "type": "float", "default": 0.8,
"label": "Brightness", "ui": {"control": "slider", "min": 0.0, "max": 1.0, "step": 0.01}}
]
}
*/
vec4 hxMain(in HxFrag hx) {
float up = clamp(0.5 + 0.5 * hx.direction.y, 0.0, 1.0);
float pulse = 0.7 + 0.3 * sin(hx.time * 2.0);
vec3 horizon = vec3(0.08, 0.18, 0.32);
vec3 zenith = vec3(0.35, 0.55, 0.90);
vec3 sky = mix(horizon, zenith, up) * hxParam_gain * pulse;
return vec4(sky, 1.0);
}
Check & Preview, then use the Workshop Timeline transport to Play, Pause or Reset. Set Start, End and FPS for the interval you want; preview Loop repeats that range. A Shader without saved timeline settings starts with 0–4 seconds and Loop on. A sequence samples once at Start + n/FPS with End exclusive; preview looping does not make a sequence repeat. hx.timeDelta, hx.frame and hx.frameRate are also supplied by the host. Read Timeline and value modes and Render Sequence and Dome output before exporting motion. Sequence preflight can refuse an invalid expression, dependency cycle or unsupported capability even if a preview appears plausible.
Inputs and controls
This first shader needs no image input, include file or interactive mapping. If you add helpers, declare the complete source set and use confined relative quoted #include paths. If you add images, the package's declared inputN slots and every sequence frame must pass validation; sampling helpers convert PNG RGB from sRGB to linear and preserve straight alpha. conventions/1 can define resolved inputs and optional camera helpers in a supporting host. Live input history and seeking differ from a frozen sequence sample. Check the format's capability and resource rules before depending on these extensions. This tutorial does not assume a MIDI, OSC or gamepad bridge.
Troubleshooting
| Symptom | Check |
|---|---|
| Header or compile refusal | Paste the entire source, including the /*@hextile-shader JSON header and closing */. Keep schema: 1, abi: "hx-shader/1", space: "direction_360", and hxMain; omit a user main(). Check the diagnostic's source line. |
| A control is missing or has the wrong type | Check the header's parameter ID, type, default and UI range. Use the generated hxParam_<id> name in GLSL and a value of the declared type. |
| Include or resource refusal | Declare each helper and resource, use portable relative paths and valid member digests. Verify every resource frame, not only the frame shown in preview. |
| Unsupported capability | Inspect the required-capability identifier and extension schema. Update to a compatible installed host or remove the dependency from your own source; never silently ignore it. |
| Preview works but sequence refuses | Run Check & Preview and sequence preflight again with the chosen interval. Inspect expression cycles, invalid samples, unsupported features and missing frames. |
| Saved output differs from a loop | Remember that End is exclusive and sequence sampling runs once. Use the Workshop sequence guide for exact rational FPS and sample count. |
To share a completed Shader, accept it first, then use Export Shader Bundle. A .hexshader contains the accepted snapshot, not an unsaved draft. Open/Add Shader Bundle loads a package into a library; Import renders a panorama. The Workshop guide covers the full journey and the format reference covers host admission.