selena

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 23, 2026 License: MIT Imports: 11 Imported by: 0

README

selena

One shader source → every surface. Selena is the shader & material authoring language for the GoSX ecosystem. You describe a material once; Selena emits the right shader for each rendering target — so a custom look renders identically in the browser, in a GoSX desktop window, on iOS, and on Android, with no per-backend shader work.

Named because she is the one who makes the light behave.

Why

GoSX scenes are authored once (Go/JSX → scene.IR) and already render everywhere through that one contract. There are two material tiers today:

  • Standard / lit materials are structured props (color, roughness, metalness, maps). Every target renders them with its own built-in shader, so they already work on WebGL, WebGPU, SceneKit, and GLES with zero extra work. This tier is seamless.
  • Custom shaders fall off a cliff: you hand-write GLSL and WGSL (and mobile gets nothing), and GoSX's WebGPU honesty gate then degrades any backend you didn't hand-author. The moment you want a look beyond the standard material, the cross-target story breaks and you're maintaining four shader dialects by hand.

Selena closes that cliff. It extends the seamless property of the standard material to custom looks: author in one high-level language, lower to a neutral shader IR, and let target emitters produce WGSL / GLSL / Metal / GLSL-ES.

Pipeline

  material.sel                          (author once — grammargen-defined DSL)
      │  parse  (gotreesitter / grammargen, the same engine behind .gsx)
      ▼
  Selena AST
      │  lower
      ▼
  Selena IR        ← the neutral, typed shader graph (the heart of the project)
      │
      ├── emit/wgsl  → WGSL        ┐ browser + GoSX desktop (Chromium / WebView2)
      ├── emit/glsl  → GLSL        ┘   via 16a-scene-webgpu.js / 16-scene-webgl.js
      ├── emit/metal → Metal (MSL) ┐ mobile via gosx-native
      └── emit/gles  → GLSL ES     ┘   SceneKit (iOS) / GLSurfaceView (Android)

Target map

The shipping surfaces collapse to two shader worlds:

Surface Shell Renderer Selena emits
Browser JS runtime (16a WebGPU / 16 WebGL) WGSL + GLSL
Desktop GoSX desktop/ (Win32 + WebView2, no cgo) same JS runtime in Chromium WGSL + GLSL (shared with browser)
Mobile gosx-native (SwiftUI / Compose) SceneKit (iOS) / GLES (Android) Metal + GLSL-ES

Because browser and desktop are both Chromium, emitting WGSL alongside GLSL lights up WebGPU on both at once — which is exactly how "reach WebGPU the same way we reach WebGL" stops being a feature chase and falls out of the compiler.

Try it

go test ./...
go run ./cmd/selena check examples/textured.sel
go run ./cmd/selena inspect examples/tinted.sel Tinted
go run ./cmd/selena inspect examples/defaults.sel
go run ./cmd/selena emit wgsl examples/directional-diffuse.sel
go run ./cmd/selena demo /tmp/selena-textured.html textured
go run ./cmd/selena demo /tmp/selena-defaults.html defaults

Open the generated demo HTML in Chrome to compare the WGSL/WebGPU path with the GLSL/WebGL path from the same .sel material.

See docs/language-guide.md for copy-pasteable material patterns, supported types, defaults, host packing, and diagnostics. See docs/compatibility.md for the current backend and host descriptor contract, and docs/standard-material-interop.md for the PBR/standard-material boundary.

Use as a library

src, err := os.ReadFile("examples/textured.sel")
if err != nil {
    return err
}
res, err := selena.Compile(src, selena.CompileOptions{})
if err != nil {
    return err
}
wgsl, _ := res.Artifact(selena.TargetWGSL)
fmt.Println(res.Layout.UniformBlock.Size, len(wgsl.Source))

CompileOptions{} emits WGSL, GLSL, Metal, and GLES in a deterministic order. Set Material to choose a named material from a file, or pass Targets: []selena.Target{} to parse and lower without emitting shader source. Use bindings.PackUniforms(res.Layout, values) to fill the generated std140 uniform block without hand-packing vec3 tails or mat3 column strides. bindings.PackUniformsWithDefaults also fills omitted uniform fields from descriptor defaults declared in .sel. Compile failures that can be tied to source return *selena.CompileError with diagnostic codes and 1-based line/column ranges. The CLI renders those ranges as annotated snippets with fix-oriented hints, and common syntax errors include expected-token context. Authored params and locals are rejected before emission when they collide with shader keywords, generated symbols, or Selena stdlib builtins. Programmatic HIR vertex hooks are also rejected explicitly until vertex-hook lowering lands, rather than being silently ignored.

How it plugs into GoSX

  • Grammar engine: gotreesitter + grammargen — the same stack that builds .gsx (Go+JSX) and .swift.gsx (Swift+JSX). The Selena DSL is the natural third member of that family.
  • Scene contract: the Selena IR is the source of truth for a material; the per-backend string slots already present on gosx/scene.IR's IRMaterial (CustomVertexWGSL, CustomFragment, …) become emitter outputs rather than authoring inputs.
  • Honesty gate: GoSX already models "which backends can serve this material," so partial emitter coverage degrades gracefully — Selena can ship one emitter at a time and the gate handles the rest.

Selena's core (grammar → IR → emitters) is intentionally standalone and dependency-free: the GoSX/scene.IR and gosx-native integrations live in thin adapter layers so the language stays reusable.

Project layout

Path Role
grammar/ the grammargen-defined Selena shader grammar (.sel)
lower/ Selena AST → Selena IR
ir/ the neutral, typed shader IR — the heart of the project
emit/wgsl/ IR → WGSL (browser + desktop WebGPU)
emit/glsl/ IR → GLSL (browser + desktop WebGL)
emit/metal/ IR → Metal MSL (iOS SceneKit)
emit/gles/ IR → GLSL ES (Android GLES)
cmd/selena/ CLI: selena emit, selena check, selena inspect, selena demo
examples/, testdata/ sample materials, conformance inputs, and golden outputs

Open design decisions

These are the calls to make before deep implementation (a dedicated design pass will resolve them):

  1. IR shape — typed expression AST vs. an explicit node-graph. Leaning typed AST with a small, total expression language (no unbounded loops) so all four emitters are straightforward and the output is deterministic.
  2. DSL surface — a standalone .sel file vs. a <Material>/shader block embedded in .gsx. Likely both: a file form for reusable materials and an inline form for one-offs.
  3. Type & binding model — how uniforms/attributes/samplers/varyings are declared once and mapped to each backend's binding conventions (WGSL bind groups vs. GLSL uniforms vs. SceneKit SCNProgram semantics).
  4. Standard-material interop — Selena materials should compose with the existing lit/PBR standard material (extend it, not replace it).
  5. Conformance — the native Go WebGPU renderer (gosx/render/bundle) stays useful as a pixel oracle to validate the WGSL emitter; golden tests per emitter otherwise.

Status

Vertical slice online. Selena now parses .sel files, lowers typed HIR into the neutral shader IR, computes host binding layouts, emits WGSL / GLSL / Metal / GLSL-ES, exposes a root Compile API, packs host uniform blocks from the descriptor, adapts into GoSX scene.IRMaterial, and compile-checks emitted WGSL and GLSL-ES where offline validators are installed.

Current compiler coverage includes directional diffuse materials, texture sampling, reusable functions, material inheritance via extends / super.surface, deterministic binding descriptors with scalar/vector defaults, matrix defaults, CLI inspectability, and source-aware semantic validation with annotated CLI snippets before backend shader emission. Function inlining and invalid super.surface usage now also report call-site diagnostics. The stdlib registry now centralizes Sun fields, geometry producers, and builtin typing metadata; backend builtin emission goes through declarative per-target spelling tables; descriptors carry explicit schema/language versions; and descriptor defaults now feed the demo harness plus GoSX CustomUniforms. Compatibility notes, a .sel conformance corpus, and parser/compile benchmarks now exist as the baseline for broader material/PBR interop and runtime conformance work. The standard-material interop boundary is now documented so future PBR hooks extend GoSX's renderer instead of forking it.

See ROADMAP.md for the public next-step plan and CONTRIBUTING.md for development notes.

Documentation

Overview

Package selena is the shader & material authoring language for the GoSX ecosystem: author a material once and emit the correct shader for every rendering target (WGSL/GLSL for browser + desktop, Metal/GLSL-ES for mobile via gosx-native).

The pipeline is: .sel source -> grammar (gotreesitter/grammargen) -> AST -> lower -> the neutral Selena IR (package ir) -> per-target emitters (package emit/...). See README.md for the architecture and design notes.

The root Compile APIs are the stable integration point for tools and hosts: parse .sel source, select a material, lower it to IR, compute the binding descriptor, and optionally emit all supported shader targets.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Artifact

type Artifact struct {
	Target   Target
	Source   string
	Vertex   string
	Fragment string
}

Artifact is the emitted shader source for one target. WGSL and Metal use Source. GLSL and GLES use Vertex and Fragment.

type CompileError

type CompileError struct {
	Diagnostics []Diagnostic
	Err         error
}

CompileError wraps one or more diagnostics while preserving the original error for errors.Is/errors.As callers.

func (*CompileError) Error

func (e *CompileError) Error() string

func (*CompileError) Unwrap

func (e *CompileError) Unwrap() error

type CompileOptions

type CompileOptions struct {
	Material string
	Targets  []Target
}

CompileOptions controls material selection and shader emission.

If Material is empty, Selena compiles the last material in the program. This matches the file-authoring convention where a derived material follows its base material. If Targets is nil, Compile emits every supported target. If Targets is an empty non-nil slice, Compile parses and lowers without emitting shader source.

type Diagnostic

type Diagnostic struct {
	Code     string
	Severity Severity
	Message  string
	Range    hir.Span
	Hint     string
}

Diagnostic is a structured compiler message. Range is zero when source information is not available, such as when callers hand-build HIR.

type Result

type Result struct {
	Program   hir.Program
	Material  hir.Material
	Module    ir.Module
	Layout    bindings.Layout
	Artifacts []Artifact
}

Result contains the useful artifacts from a Selena compile: parsed HIR, selected material, lowered IR, host binding layout, and emitted shader source.

func Compile

func Compile(source []byte, opts CompileOptions) (Result, error)

Compile parses source, selects a material, lowers it, and emits requested shader targets.

func CompileMaterial

func CompileMaterial(p hir.Program, name string, opts CompileOptions) (Result, error)

CompileMaterial compiles a named material from an already parsed program.

func CompileProgram

func CompileProgram(p hir.Program, opts CompileOptions) (Result, error)

CompileProgram selects, lowers, and emits one material from p.

func (Result) Artifact

func (r Result) Artifact(target Target) (Artifact, bool)

Artifact returns the emitted artifact for target.

type Severity

type Severity string

Severity classifies a compiler diagnostic.

const (
	SeverityError Severity = "error"
)

type Target

type Target string

Target is one shader backend Selena can emit.

const (
	TargetWGSL  Target = "wgsl"
	TargetGLSL  Target = "glsl"
	TargetMetal Target = "metal"
	TargetGLES  Target = "gles"
)

func AllTargets

func AllTargets() []Target

AllTargets returns Selena's default backend set in deterministic order.

Directories

Path Synopsis
adapter
gosx
Package gosx adapts a Selena material into a GoSX scene.IRMaterial, filling the custom-shader slots for every backend (GLSL for WebGL, WGSL for WebGPU).
Package gosx adapts a Selena material into a GoSX scene.IRMaterial, filling the custom-shader slots for every backend (GLSL for WebGL, WGSL for WebGPU).
Package bindings computes and represents the host-facing binding layout for a material: the std140 uniform-block byte map, vertex attributes, textures, and per-backend binding coordinates.
Package bindings computes and represents the host-facing binding layout for a material: the std140 uniform-block byte map, vertex attributes, textures, and per-backend binding coordinates.
cmd
selena command
Command selena is the CLI for the Selena shader authoring language.
Command selena is the CLI for the Selena shader authoring language.
selena-grammar command
Command selena-grammar regenerates the embedded parser blob used by parse.
Command selena-grammar regenerates the embedded parser blob used by parse.
emit
gles
Package gles emits GLSL ES from the Selena IR for the Android GLSurfaceView backend, consumed by gosx-native.
Package gles emits GLSL ES from the Selena IR for the Android GLSurfaceView backend, consumed by gosx-native.
glsl
Package glsl emits GLSL from the Selena IR for the WebGL backend (browser + GoSX desktop, via 16-scene-webgl.js running in Chromium).
Package glsl emits GLSL from the Selena IR for the WebGL backend (browser + GoSX desktop, via 16-scene-webgl.js running in Chromium).
internal
Package internal holds helpers shared by the four backend shader emitters (emit/wgsl, emit/glsl, emit/metal, emit/gles).
Package internal holds helpers shared by the four backend shader emitters (emit/wgsl, emit/glsl, emit/metal, emit/gles).
metal
Package metal emits Metal Shading Language from the Selena IR for the iOS SceneKit backend, consumed by gosx-native.
Package metal emits Metal Shading Language from the Selena IR for the iOS SceneKit backend, consumed by gosx-native.
wgsl
Package wgsl emits WGSL from the Selena IR for the WebGPU backend (browser + GoSX desktop, via 16a-scene-webgpu.js running in Chromium).
Package wgsl emits WGSL from the Selena IR for the WebGPU backend (browser + GoSX desktop, via 16a-scene-webgpu.js running in Chromium).
Package grammar defines the Selena shader DSL grammar using gotreesitter + grammargen — the same grammar engine behind GoSX's .gsx and gosx-native's .swift.gsx.
Package grammar defines the Selena shader DSL grammar using gotreesitter + grammargen — the same grammar engine behind GoSX's .gsx and gosx-native's .swift.gsx.
Package hir is Selena's high-level, author-facing material model — the level a human (or grammar front-end) targets.
Package hir is Selena's high-level, author-facing material model — the level a human (or grammar front-end) targets.
Package ir defines the neutral, typed Selena shader IR — the heart of the project and the single source of truth for a material.
Package ir defines the neutral, typed Selena shader IR — the heart of the project and the single source of truth for a material.
Package lower translates a parsed Selena AST into the neutral Selena IR (package ir), resolving types, uniform/attribute/sampler/varying bindings, and standard-material composition.
Package lower translates a parsed Selena AST into the neutral Selena IR (package ir), resolving types, uniform/attribute/sampler/varying bindings, and standard-material composition.
Package parse turns .sel source into the high-level material model (hir).
Package parse turns .sel source into the high-level material model (hir).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL