>) - Optional - Pass-specific uniforms. Can be array (indexed by pass order) or object (keyed by pass name).
### Behavior
- Creates command encoder and render passes
- Merges global uniforms with per-pass uniforms
- Resolves input textures from previous passes
- Automatically builds appropriate bind groups based on pass type (bg, blur, main)
- Submits command buffer to GPU queue
### Array Format Usage
```typescript
renderer.render([
{ u_bgType: 0 },
{ u_prevPassTexture: texture1 },
{ u_prevPassTexture: texture2 },
{ u_blurredBg: texture3 }
]);
```
### Object Format Usage
```typescript
renderer.render({
bgPass: { u_bgType: 2 },
mainPass: { u_tint: [1, 1, 1, 0.5] }
});
```
```
--------------------------------
### useLevaControls Hook Usage Example
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-controls.md
Demonstrates how to use the useLevaControls hook in a React component. It shows how to pass custom render components and renderer options, and how to destructure the returned controls and API.
```typescript
const { controls, controlsAPI, lang, levaGlobal } = useLevaControls({
containerRender: {
bgType: ({ value, setValue }) => (
setValue(value + 1)}>Select Background
)
},
rendererOptions: {
webgpuSupported: true,
onRendererChange: (backend) => console.log('Switched to', backend)
}
});
```
--------------------------------
### Import and Validate Preset
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/quick-reference.md
Import a preset configuration file and log its version, timestamp, and available control keys. This helps in validating the preset's structure and content.
```typescript
const preset = await importPreset(file);
console.log('Preset version:', preset.version);
console.log('Preset timestamp:', preset.timestamp);
console.log('Control keys:', Object.keys(preset.controls));
```
--------------------------------
### OpenSpec Scenario Formatting - Correct
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/openspec/AGENTS.md
Correct markdown formatting for scenarios within spec files, using '####' headers and specific WHEN/THEN keywords.
```markdown
#### Scenario: User login success
- **WHEN** valid credentials provided
- **THEN** return JWT token
```
--------------------------------
### Get Output Texture from a Pass
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-shader-and-pass.md
Retrieves the color texture rendered by a pass. Use this to pass the output of one pass as input to another. Returns null if the pass outputs directly to the screen.
```typescript
const outputTexture = pass.getOutputTexture();
if (outputTexture) {
nextPass.render({ u_prevPassTexture: outputTexture });
}
```
--------------------------------
### Create RenderPass
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-shader-and-pass.md
Initializes a RenderPass. It automatically sets up a ShaderProgram, framebuffer (if not rendering to screen), and a VAO for a fullscreen quad.
```typescript
import { RenderPass } from './utils/GLUtils';
const pass = new RenderPass(gl, shaderSource, false);
// Renders to framebuffer by default
```
--------------------------------
### Project Structure
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/architecture-overview.md
Illustrates the directory layout of the Liquid Glass Studio project, highlighting key files and directories for components, shaders, utilities, and assets.
```tree
src/
├── App.tsx # Main React component, renderer orchestration
├── Controls.tsx # Leva control panel management
├── main.tsx # React app entry point
├── components/
│ ├── LevaButton/ # Custom button component
│ ├── LevaCheckButtons/ # Custom button group component
│ ├── LevaContainer/ # Custom container component
│ ├── LevaImageUpload/ # Image upload component (unused)
│ ├── LevaVectorNew/ # Custom vector input component
│ ├── PresetControls/ # Preset import/export UI
│ └── ResizableWindow/ # Draggable window wrapper
├── shaders/ # GLSL 3.00 ES shaders
│ ├── vertex.glsl # Fullscreen quad vertex shader
│ ├── fragment-bg.glsl # Background rendering shader
│ ├── fragment-main.glsl # Main glass effect shader
│ ├── fragment-bg-vblur.glsl # Vertical blur shader
│ └── fragment-bg-hblur.glsl # Horizontal blur shader
├── shaders-wgsl/ # WGSL shaders (WebGPU)
│ ├── vertex.wgsl
│ ├── fragment-bg.wgsl
│ ├── fragment-main.wgsl
│ ├── fragment-bg-vblur.wgsl
│ └── fragment-bg-hblur.wgsl
├── utils/
│ ├── GLUtils.ts # WebGL2 renderer & shader utilities
│ ├── GPUUtils.ts # WebGPU renderer
│ ├── RendererInterface.ts # Common types & interfaces
│ ├── gpuDetect.ts # WebGPU capability detection
│ ├── index.ts # Utility function exports
│ ├── languages.ts # Localization strings
│ ├── presetUtils.ts # Preset save/load
│ └── useResizeObserver.ts # React resize hook
└── assets/ # Images and videos
```
--------------------------------
### Load Texture from URL (WebGPU)
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/quick-reference.md
Load an image texture from a URL for use with WebGPU. Also demonstrates creating an empty texture for video and updating it, which may return a new texture.
```typescript
import {
gpuLoadTextureFromURL,
gpuCreateEmptyTexture,
gpuUpdateVideoTexture
} from './utils/GPUUtils';
// Load image
const { texture, ratio } = await gpuLoadTextureFromURL(device, imageUrl);
// Create empty for video
const videoTexture = gpuCreateEmptyTexture(device, width, height);
// Update video each frame
const info = await gpuUpdateVideoTexture(device, videoTexture, videoElement);
if (info) {
videoTexture = info.texture; // May be new texture
}
```
--------------------------------
### Detect and Use WebGPU
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/quick-reference.md
Asynchronously detect if WebGPU is supported. If supported, initialize the renderer with the detected device; otherwise, log the reason and fall back to WebGL2. Cached results can also be retrieved.
```typescript
import { detectWebGPU, getWebGPUDetectResult } from './utils/gpuDetect';
// Async detection
const result = await detectWebGPU();
if (result.supported && result.device) {
// Use WebGPU
renderer = new GPUMultiPassRenderer(canvas, configs, result.device);
} else {
console.log(result.reason); // Why not supported
// Fall back to WebGL2
}
// Or get cached result
const cachedResult = getWebGPUDetectResult();
```
--------------------------------
### Import Preset Configuration
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/INDEX.md
Loads a saved configuration from a JSON object. This restores the application's settings to a previous state.
```typescript
import { importPreset } from "./utils/presetUtils";
const presetJson = '{"param1": 10, "param2": "value"}'; // Example preset data
const presetData = JSON.parse(presetJson);
importPreset(presetData);
console.log("Preset imported successfully.");
```
--------------------------------
### Build Scripts for Liquid Glass Studio
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/configuration.md
Defines the npm scripts for building, developing, linting, and previewing the Liquid Glass Studio project.
```json
{
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
}
}
```
--------------------------------
### Texture Loading Lifecycle
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-app.md
Outlines the process from setting a background texture URL to its display in the shader. This involves asynchronous loading and updating readiness flags.
```text
bgTextureUrl set (user selects background)
↓
Render loop detects change
↓
Load texture (async)
↓
bgTextureReady = true
↓
Shader receives texture
↓
Display updates
```
--------------------------------
### Key Imports: Types
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/quick-reference.md
Imports type definitions for renderers, textures, and presets, including WebGPU detection results.
```typescript
import type {
IMultiPassRenderer,
RenderPassConfig,
ITextureHandle,
TextureLoadResult
} from './utils/RendererInterface';
import type { WebGPUDetectResult } from './utils/gpuDetect';
import type { PresetData } from './utils/presetUtils';
```
--------------------------------
### LevaButton Component Usage
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-custom-components.md
Demonstrates how to import and use the LevaButton component for triggering actions, including passing an onClick handler and children for content.
```typescript
// Import and usage in PresetControls.tsx
import { LevaButton } from './components/LevaButton/LevaButton';
Export
```
--------------------------------
### gpuCreateEmptyTexture
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-texture-utils.md
Create an empty WebGPU texture for later filling. Allows specifying initial width and height, and the texture is created with texture binding and copy usage flags.
```APIDOC
## gpuCreateEmptyTexture
### Description
Create an empty WebGPU texture for later filling. Allows specifying initial width and height, and the texture is created with texture binding and copy usage flags.
### Parameters
#### Path Parameters
- None
#### Query Parameters
- None
#### Request Body
- None
### Parameters
- **device** (GPUDevice) - Required - WebGPU device
- **width** (number) - Optional - Initial width (default: 1)
- **height** (number) - Optional - Initial height (default: 1)
### Response
#### Success Response (200)
- **texture** (GPUTexture) - GPUTexture with texture binding and copy usage
### Request Example
```typescript
let videoTexture = gpuCreateEmptyTexture(device, videoWidth, videoHeight);
```
### Response Example
```json
{
"texture": "GPUTexture object"
}
```
```
--------------------------------
### Initialize Leva Controls
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-app.md
Initializes the Leva control panel, managing renderer options and custom container rendering. It returns control values, language settings, and an API for updating controls.
```typescript
const { controls, lang, langName, levaGlobal, controlsAPI } = useLevaControls({
rendererOptions: {
webgpuSupported: webgpuDetect?.supported ?? false,
webgpuUnavailableReason: webgpuDetect?.reason,
onRendererChange: (backend) => {
setRendererBackend(backend);
setCanvasKey((k) => k + 1);
},
},
containerRender: {
bgType: ({ value, setValue }) => (
// Custom background selection UI
),
},
});
```
--------------------------------
### OpenSpec CLI Essentials
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/openspec/AGENTS.md
Commonly used OpenSpec CLI commands for listing changes, viewing details, validating, and archiving.
```bash
openspec list # What's in progress?
```
```bash
openspec show [item] # View details
```
```bash
openspec validate --strict # Is it correct?
```
```bash
openspec archive [--yes|-y] # Mark complete (add --yes for automation)
```
--------------------------------
### Load Background Texture with Fallback
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/errors-and-exceptions.md
Loads a texture for the background, attempting WebGL2 or WebGPU based on backend availability. Logs errors and returns null on failure.
```typescript
async function loadBackgroundTexture(url: string) {
try {
if (backend === 'webgl') {
const gl = canvas.getContext('webgl2');
const { texture, ratio } = await loadTextureFromURL(gl, url);
return { texture, ratio };
} else {
const { device } = await detectWebGPU();
return await gpuLoadTextureFromURL(device, url);
}
} catch (err) {
console.error('Texture load failed:', url, err);
// Use placeholder or fallback
return null;
}
}
```
--------------------------------
### Component Integration Pattern in useLevaControls
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-custom-components.md
Illustrates the common pattern for integrating custom Leva components within the useLevaControls hook, showing how components return Leva configurations.
```typescript
const [controls, controlsAPI] = useControls(() => ({
'basicSettings': folder({
renderer: LevaCheckButtons({...}), // Returns Leva config
language: LevaCheckButtons({...}),
}),
shadowPosition: LevaVectorNew({...}), // Returns Leva config
bgType: LevaContainer({...}), // Returns Leva config
// ... more controls
}));
```
--------------------------------
### Initialize MultiPassRenderer
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-multipass-renderer.md
Instantiate the MultiPassRenderer with a target canvas and an array of render pass configurations. Ensure WebGL2 context and the EXT_color_buffer_float extension are available.
```typescript
import { MultiPassRenderer } from './utils/GLUtils';
const configs = [
{ name: 'bgPass', shader: { vertex: vertShader, fragment: fragShader } },
{ name: 'blurPass', shader: { vertex: vertShader, fragment: blurShader },
inputs: { u_prevPassTexture: 'bgPass' } },
{ name: 'mainPass', shader: { vertex: vertShader, fragment: mainShader },
inputs: { u_blurredBg: 'blurPass' }, outputToScreen: true }
];
const renderer = new MultiPassRenderer(canvas, configs);
```
--------------------------------
### Handle Video Not Ready
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/errors-and-exceptions.md
Check the return value of `updateVideoTexture` to determine if the video has loaded enough data. If it returns a falsy value, the video is still loading and should be retried.
```typescript
const result = updateVideoTexture(gl, texture, video);
if (result) {
renderer.setUniform('u_bgTextureRatio', result.ratio);
} else {
// Video still loading, try again next frame
}
```
--------------------------------
### Handle WebGPU Context Unavailable (TypeScript)
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/errors-and-exceptions.md
Catches errors during WebGPU context initialization, allowing for a fallback to the WebGL2 renderer if WebGPU is not available or fails to initialize.
```typescript
try {
renderer = new GPUMultiPassRenderer(canvas, configs, device);
} catch (err) {
console.error('WebGPU initialization failed:', err);
// Fall back to WebGL2
}
```
--------------------------------
### Interactive Show Command
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/openspec/AGENTS.md
Running 'openspec show' without arguments will prompt the user to select which change or spec they want to view details for.
```bash
openspec show
```
--------------------------------
### Export and Import Presets
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/quick-reference.md
Export the current control settings to a JSON file and import a preset file to apply settings. Includes basic error handling for import.
```typescript
import { exportPreset, importPreset } from './utils/presetUtils';
// Export
exportPreset(controls, 'my-preset.json');
// Import
try {
const preset = await importPreset(file);
controlsAPI(preset.controls);
} catch (err) {
console.error('Import failed:', err.message);
}
```
--------------------------------
### Create WebGPU Renderer
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-app.md
A useCallback hook that creates a WebGPU renderer. It utilizes the same pass configuration as the WebGL renderer but employs WGSL shaders for the GPU pipeline.
```typescript
const createWebGPURenderer = useCallback((canvasEl: HTMLCanvasElement, device: GPUDevice) => {
return new GPUMultiPassRenderer(canvasEl, [
{ name: 'bgPass', shader: { vertex: WgslVertex, fragment: WgslFragBg } },
{ name: 'vBlurPass', shader: { vertex: WgslVertex, fragment: WgslFragVblur },
inputs: { u_prevPassTexture: 'bgPass' } },
{ name: 'hBlurPass', shader: { vertex: WgslVertex, fragment: WgslFragHblur },
inputs: { u_prevPassTexture: 'vBlurPass' } },
{ name: 'mainPass', shader: { vertex: WgslVertex, fragment: WgslFragMain },
inputs: { u_blurredBg: 'hBlurPass', u_bg: 'bgPass' }, outputToScreen: true },
], device);
}, []);
```
--------------------------------
### Backend Selection Logic
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/architecture-overview.md
Selects the appropriate renderer (WebGL or WebGPU) based on the chosen backend and browser support. The canvas must be remounted when switching renderers due to context exclusivity.
```typescript
const [rendererBackend, setRendererBackend] = useState<'webgl' | 'webgpu'>('webgl');
useEffect(() => {
if (rendererBackend === 'webgpu' && webgpuDetect?.supported) {
renderer = createWebGPURenderer(canvas, device);
} else {
renderer = createWebGLRenderer(canvas);
}
// Note: Canvas must be remounted (destroyed/recreated) when switching
// because WebGL2 and WebGPU contexts are mutually exclusive
}, [rendererBackend, canvasKey, webgpuDetect]);
```
--------------------------------
### Key Imports: Utility Functions
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/quick-reference.md
Imports various utility functions for tasks like Gaussian kernel computation, language detection, string capitalization, WebGPU detection, and preset management.
```typescript
import {
computeGaussianKernelByRadius,
isChineseLanguage,
isUzbekLanguage,
capitalize
} from './utils/index';
import { detectWebGPU, getWebGPUDetectResult } from './utils/gpuDetect';
import { exportPreset, importPreset } from './utils/presetUtils';
```
--------------------------------
### Load Texture from URL (WebGL2)
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/quick-reference.md
Load an image texture from a URL for use with WebGL2. Also shows how to create an empty texture for video and update it.
```typescript
import { loadTextureFromURL, createEmptyTexture, updateVideoTexture }
from './utils/GLUtils';
// Load image
const { texture, ratio } = await loadTextureFromURL(gl, imageUrl);
// Create empty for video
const videoTexture = createEmptyTexture(gl);
// Update video each frame
const info = updateVideoTexture(gl, videoTexture, videoElement);
```
--------------------------------
### importPreset
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-controls.md
Loads control settings from a JSON preset file. This function is essential for restoring previously saved states or applying new configurations.
```APIDOC
## importPreset
### Description
Loads control settings from a JSON preset file. This function is essential for restoring previously saved states or applying new configurations.
### Method
```typescript
export function importPreset(file: File): Promise
```
### Parameters
#### Path Parameters
- **file** (File) - Required - Preset JSON file
### Response
#### Success Response
- **PresetData** (Promise) - Promise resolving to PresetData object
#### Error Handling
- Promise rejects with descriptive error if:
- File is not valid JSON
- JSON lacks `version` or `controls` fields
- File read fails
### Usage
```typescript
const handleImport = async (file: File) => {
try {
const preset = await importPreset(file);
controlsAPI(preset.controls);
} catch (err) {
alert(`Import failed: ${err.message}`);
}
};
```
```
--------------------------------
### RenderPass Constructor
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-shader-and-pass.md
Initializes a RenderPass, setting up a shader program and optionally a framebuffer for rendering. By default, it renders to an internal framebuffer unless `outputToScreen` is set to true.
```APIDOC
## RenderPass Constructor
### Description
Manages a single render pass with framebuffer, shader program, and VAO. Initializes a RenderPass, setting up a shader program and optionally a framebuffer for rendering. By default, it renders to an internal framebuffer unless `outputToScreen` is set to true.
### Parameters
- **gl** (WebGL2RenderingContext) - WebGL2 rendering context
- **shaderSource** (ShaderSource) - Vertex and fragment shader source
- **outputToScreen** (boolean) - If true, renders to screen; if false, to framebuffer (default: false)
### Automatic Setup
- Creates and configures ShaderProgram
- Creates framebuffer with RGBA16F color and DEPTH_COMPONENT24 depth if not outputToScreen
- Creates VAO with fullscreen quad geometry
### Usage
```typescript
import { RenderPass } from './utils/GLUtils';
const pass = new RenderPass(gl, shaderSource, false);
// Renders to framebuffer by default
```
```
--------------------------------
### Import Preset with Error Handling
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/errors-and-exceptions.md
Handles the import of a preset file, updating controls and showing success or failure alerts. Displays user-friendly error messages.
```typescript
const handleImport = async (file: File) => {
try {
const preset = await importPreset(file);
controlsAPI(preset.controls);
alert('Preset loaded successfully');
} catch (err) {
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
alert(`Failed to import preset: ${errorMsg}`);
}
};
```
--------------------------------
### Detect WebGPU Support on Mount
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/api-reference-app.md
Uses `useEffect` to detect WebGPU support and acquire the GPU device once when the component mounts. This ensures WebGPU capabilities are available if supported.
```typescript
useEffect(() => {
detectWebGPU().then(setWebgpuDetect);
}, []);
```
--------------------------------
### Dual-Backend Architecture Diagram
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/architecture-overview.md
Illustrates the separation of concerns between the React layer, the rendering abstraction, and the underlying WebGL2 and WebGPU APIs.
```text
┌─────────────────────────────────────────────────┐
│ App.tsx (React Layer) │
│ - Canvas management, state, lifecycle │
│ - Texture loading and updates │
│ - Input handling (mouse, file upload) │
└────────────────────┬────────────────────────────┘
│
┌──────────┴──────────┐
↓ ↓
┌──────────────┐ ┌──────────────┐
│ MultiPass │ │ GPUMulti │
│ Renderer │ │ PassRenderer │
│ (WebGL2) │ │ (WebGPU) │
└──────┬───────┘ └───────┬──────┘
↓ ↓
┌──────────────┐ ┌──────────────┐
│ ShaderProgram│ │ GPURender │
│ RenderPass │ │ PassObj │
│ FrameBuffer │ │ GPUFrameBuffer
└──────┬───────┘ └───────┬──────┘
↓ ↓
WebGL2 API WebGPU API
```
--------------------------------
### Update All Dependencies
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/configuration.md
Updates all project dependencies to their latest compatible versions using pnpm.
```bash
pnpm update
```
--------------------------------
### Display Change or Spec Details
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/openspec/AGENTS.md
Use this command to display detailed information about a specific change or specification. Replace '[item]' with the change ID or spec ID.
```bash
openspec show [item]
```
--------------------------------
### WebGPUDetectResult Interface
Source: https://github.com/iyinchao/liquid-glass-studio/blob/main/_autodocs/types.md
Result of WebGPU capability detection, indicating support and providing adapter/device information if available.
```APIDOC
## WebGPUDetectResult Interface
### Description
Result of WebGPU capability detection.
### Fields
- **supported** (boolean) - Whether WebGPU is available and working
- **reason** (string) - Human-readable error message if not supported
- **adapter** (GPUAdapter) - GPU adapter (present if supported)
- **device** (GPUDevice) - Active GPU device (present if supported)
### Examples
```typescript
// Successful detection
{
supported: true,
adapter: GPUAdapter,
device: GPUDevice
}
// Failed detection
{
supported: false,
reason: 'WebGPU API not available'
}
{
supported: false,
reason: 'No GPU adapter found'
}
```
```