Skip to content
Mathis Brossier edited this page Jan 30, 2025 · 14 revisions

WESL is designed to work with both JavaScript and Rust. We have two distinct implementations for these languages, wesl-js and wesl-rs.

Wesl can be operated in a few different ways:

  • Using the standalone Command-Line tool to generate WGSL files.
  • At build-time, using a Vite Plugin (JavaScript) or a Build Script (Rust).
  • At run-time, using the linker libraries.

Setup for JavaScript users

(TODO)

Setup for Rust users

wesl-rs documentation: (TODO link)

Using the standalone CLI

  • Install the CLI: cargo install --git https://github.com/wgsl-tooling-wg/wesl-rs wesl-cli
  • Compile a shader: wesl compile <path/to/shader.wesl>
  • Type wesl --help or visit the crate documentation (coming soon) for more configuration options.

Using WESL at compile-time

  • Add wesl-rs to your build-dependencies: cargo add --git https://github.com/wgsl-tooling-wg/wesl-rs --build wesl
  • Create the file build.rs right next to your Cargo.toml.
  • Paste this content:
    fn main() {
        wesl::Wesl::new_spec_compliant("src/shaders").build_artefact("main.wesl", "my_shader");
    }
  • Place your shader in src/shaders/main.wesl.
  • Paste this code where you want to access your shader string in Rust code:
    use wesl::include_wesl;
    const shader_string = include_wesl!("my_shader");

Using WESL at run-time

  • Add wesl-rs to your dependencies: cargo add --git https://github.com/wgsl-tooling-wg/wesl-rs wesl
  • Place your shader in src/shaders/main.wesl.
  • Paste this code where you want to access your shader string in Rust code:
    let shader_string = Wesl::new_spec_compliant("src/shaders")
        .compile("main.wesl")
        .inspect_err(|e| eprintln!("WESL error: {e}")) // pretty errors with `display()`
        .unwrap()
        .to_string();

WESL Shaders in a nutshell

WESL currently provides these functionalities:

  • Imports allow splitting code in multiple files.
  • Packages are collections of general-purpose shader modules published to npm (JavaScript) and crates.io (Rust).
  • Conditional Translation is a mechanism to conditionally include/exclude chunks of code. Similar to #ifdef (C/C++) or #[cfg] (Rust).
  • ...More Coming Soon!

Import Statements

Import statements are placed at the top of your file. All they do is add a declaration present in another file to the scope.

import sdf::primitives::box;         // import function `box` in module `primitives` in package `sdf`.
import package::bindings::mySampler; // import declaration `mySampler` from file `bindings.wesl` located at the root of this package.
import super::util;                  // import the entire sibling module `./util.wesl`.
import package::animals::{           // you can nest imports with `{ }`.
    bird,
    mammals::{ dog, cat }
};
  • What can be imported? - You can import all top-level declarations in a file: functions, structs, variables and aliases. You can also import whole modules.
  • How to locate a file? - The first segment indicate where to look for the file:
    • If it is super, start from the folder containing the current file. You can chain supers to walk up the file tree.
    • If it is package, start at the package root (provided to the linker).
    • Any other name corresponds to an external package.

Qualified names

In addition to import statements, you can write directly the path to a declaration at the usage site.

import package::math;

fn main() {
    package::my_mod::my_submod::my_fn(); // import statement not required.
    let x = math::constants::PI;         // module `math` was imported, you can access its declarations with a qualified name.
}

@if attribute

The @if(condition) attribute makes the following element optionally included in the final shader, depending on the condition. The condition is made of feature flags which are set when you invoke the linker (wesl-js or [wesl-rs]).

@if(debug_mode && !raytracing_disabled) // attributes on top-level declarations 
var<private> debug_count_iterations: i32 = 0;

@if(is_2d)
alias point = vec2f;
@if(!is_2d) // the @else block is in discussion, meanwhile just negate the if condition.
alias point = vec3f;

struct Line {
    start_point: point, end_point: point,
    @if(colored) color: vec4f,
}

fn fibonacci(n: u32) {
    @if(use_lookup_table) { // attributes on blocks `{ }`
        const lut = array(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144);
        return lut[n];
    }
    // ...
}

The condition is a boolean expression containing: feature names, && (and), || (or), ! (not), true and false.

You can place attributes on declarations (functions, structs, variables, etc.), statements, struct members and function parameters.

Next Steps

Visit the Reference page for the complete documentation of WESL Extensions.

Documentation has moved → wesl-lang.dev

This wiki is no longer maintained.

Clone this wiki locally