Quick Start
Write and compile your first BWSL shader in 5 minutes.
Turn the guide into code
Take the key idea from this page into the playground and validate it in a real shader instead of leaving it as theory.
Open PlaygroundLet's build a simple hologram effect and compile it end to end. This page assumes bwslc is already on your PATH; if you built from source and have not installed it, use ./build/bwslc from the compiler checkout.
Create Your First Shader
Create a file called hologram.bwsl:
pipeline Hologram {
enum HologramPalette {
Cyan
Amber
}
attributes {
position: float3
normal: float3
texcoord: float2
}
resources {
modelViewMatrix: mat4
projectionMatrix: mat4
normalMatrix: mat3
time: float
}
variants {
pulseBoost: bool = false;
palette: HologramPalette = HologramPalette::Cyan;
}
pass "MainPass" {
use attributes { position, normal, texcoord }
use resources { modelViewMatrix, projectionMatrix, normalMatrix, time }
vertex {
float4 worldPos = resources.modelViewMatrix * float4(attributes.position, 1.0);
output.position = resources.projectionMatrix * worldPos;
output.worldPos = worldPos.xyz;
output.normal = resources.normalMatrix * attributes.normal;
}
fragment {
float3 n = normalize(input.normal);
float3 viewDir = normalize(-input.worldPos);
float fresnel = pow(1.0 - saturate(dot(n, viewDir)), 2.5);
float scanline = sin(input.worldPos.y * 60.0 + resources.time * 8.0);
scanline = smoothstep(0.3, 0.7, scanline * 0.5 + 0.5);
float3 color = variants.palette == HologramPalette::Amber
? float3(1.0, 0.65, 0.1)
: float3(0.0, 0.85, 1.0);
float intensity = (fresnel + 0.15) * scanline;
if (variants.pulseBoost) {
intensity = intensity * 1.35;
}
output.color = float4(color * intensity, fresnel * 0.8 + 0.2);
}
}
}
Declare Resources in Source
resources {} is part of the pipeline. That block declares the shader-visible values the host must provide.
For this shader:
modelViewMatrix,projectionMatrix, andnormalMatrixare engine transformstimeis a host-driven animation value
Host Metadata Stays Outside BWSL
Render-graph details such as targets, pass ordering, or preview-only bindings are host concerns. In the docs playground those live in preview JSON, not in the language itself.
Add Compile-Time Variants
The variants {} block declares shader choices the compiler can specialize away. In this shader:
pulseBoostis a boolean variant that changes intensitypaletteis backed by theHologramPaletteenum, so tools can show named choices instead of free-form strings
Variants are accessed through the variants namespace inside stage code. When you compile a concrete selection, inactive branches become constants or disappear from the generated output.
Compile the Shader
Run the compiler directly on the shader source:
bwslc hologram.bwsl -o output/ -gles
CLI Options
SPIR-V is always generated. Flags such as -metal, -hlsl, -glsl, and -gles add translated source outputs. See the CLI Reference for the full command-line surface.
This generates:
output/hologram_pass0_vert.spv— SPIR-V vertex shaderoutput/hologram_pass0_frag.spv— SPIR-V fragment shaderoutput/hologram_pass0_vert.gles— GLSL ES vertex shaderoutput/hologram_pass0_frag.gles— GLSL ES fragment shaderoutput/hologram_pass0_vert.jsonandoutput/hologram_pass0_frag.json— stage metadata
Add -bindings when you also want a pass-level resource binding summary:
bwslc hologram.bwsl -o output/ -gles -bindings
That writes output/hologram_pass0.bindings.json.
Compile a concrete variant with repeatable -variant flags:
bwslc hologram.bwsl -o output/ -gles -variant pulseBoost=true -variant palette=Amber
Use -dump-variant-space when tooling needs to inspect the available variant values without compiling shader outputs:
bwslc hologram.bwsl -dump-variant-space
Understanding the Structure
Pipeline Architecture
Every BWSL shader starts with a pipeline declaration. Pipelines contain one or more pass blocks, each representing a render pass.
Key Concepts
| Concept | Description |
|---|---|
| Attributes | Vertex input data such as position, normals, and UVs |
| Resources | Shader-visible values declared in source and provided by the host |
| Variants | Compile-time choices that specialize one pipeline into concrete shader permutations |
| Enums | Named option sets for variants, pattern logic, and strongly typed shader state |
| Passes | Individual render passes in your pipeline |
| Reflection | Compiler output describing resolved bindings and WebGL/backend metadata |
Use in Your Engine
Here's a minimal WebGL-style sketch:
const vertSource = await fetch('output/hologram_pass0_vert.gles').then((r) => r.text());
const fragSource = await fetch('output/hologram_pass0_frag.gles').then((r) => r.text());
const program = createShaderProgram(gl, vertSource, fragSource);
gl.uniformMatrix4fv(gl.getUniformLocation(program, 'modelViewMatrix'), false, modelViewMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(program, 'projectionMatrix'), false, projectionMatrix);
gl.uniformMatrix3fv(gl.getUniformLocation(program, 'normalMatrix'), false, normalMatrix);
gl.uniform1f(gl.getUniformLocation(program, 'time'), performance.now() / 1000);
The host can also use the generated reflection JSON to automate binding setup instead of hard-coding names.
Next Steps
- Learn Pipelines in more depth
- Explore Shader Variants
- Read about Enums and Eval
- Read about Resources
- Browse the Intrinsics
- Try the Playground