Getting Started3 min read

Quick Start

Write and compile your first BWSL shader in 5 minutes.

Reading Time
3 min
Word Count
435
Sections
8
Try It Live

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 Playground

Let'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:

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, and normalMatrix are engine transforms
  • time is 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:

  • pulseBoost is a boolean variant that changes intensity
  • palette is backed by the HologramPalette enum, 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:

bash
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 shader
  • output/hologram_pass0_frag.spv — SPIR-V fragment shader
  • output/hologram_pass0_vert.gles — GLSL ES vertex shader
  • output/hologram_pass0_frag.gles — GLSL ES fragment shader
  • output/hologram_pass0_vert.json and output/hologram_pass0_frag.json — stage metadata

Add -bindings when you also want a pass-level resource binding summary:

bash
bwslc hologram.bwsl -o output/ -gles -bindings

That writes output/hologram_pass0.bindings.json.

Compile a concrete variant with repeatable -variant flags:

bash
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:

bash
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

ConceptDescription
AttributesVertex input data such as position, normals, and UVs
ResourcesShader-visible values declared in source and provided by the host
VariantsCompile-time choices that specialize one pipeline into concrete shader permutations
EnumsNamed option sets for variants, pattern logic, and strongly typed shader state
PassesIndividual render passes in your pipeline
ReflectionCompiler output describing resolved bindings and WebGL/backend metadata

Use in Your Engine

Here's a minimal WebGL-style sketch:

javascript
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