What is it?
This is a boilerplate project intended to drop into any React project to get you started with a React Three Fiber project, WebGPU and Three.JS (TSL).
React Three Fiber (R3F)
- WebGPU SceneWebGPU scene boilerplate for React Three Fiber creative coding projects. Jump straight into building with TSL shaders.
- Sketch ComponentTemplate React component for building creative coding sketches with Three.js and TSL. Copy, paste, start creating.
Once a React project is created, you can drop this entire file into your project to get a basic full screen WebGPU scene. If you're using the Fragments Boilerplate Github Repo, this will already be set up.
import { Canvas, useThree } from '@react-three/fiber'
import { AdaptiveDpr, OrthographicCamera, Preload, StatsGl } from '@react-three/drei'
import { useState, useEffect } from 'react'
import { WebGPURenderer, LinearSRGBColorSpace, NoToneMapping } from 'three/webgpu'
type SceneProps = {
debug?: boolean
frameloop?: 'always' | 'demand' | 'never'
} & any
/**
* ColorSpaceCorrection
*
* Sets the renderer's outputColorSpace to LinearSRGBColorSpace and disables tone mapping.
* Ensures correct color output for WebGPU rendering.
*
* @returns {null}
*/
const ColorSpaceCorrection = () => {
const { set } = useThree((state) => state)
useEffect(() => {
set((state) => {
const _state = { ...state }
_state.gl.outputColorSpace = LinearSRGBColorSpace
_state.gl.toneMapping = NoToneMapping
return _state
})
}, [])
return null
}
/**
* WebGPUScene
*
* Renders a three.js scene using the WebGPURenderer inside a @react-three/fiber Canvas.
*
* @param {SceneProps} props - Scene configuration props
* @param {boolean} [props.debug=false] - Show WebGL stats overlay
* @param {'always'|'demand'|'never'} [props.frameloop='always'] - Canvas render loop mode
* @param {boolean} [props.orthographic=false] - Use orthographic camera (not currently used)
* @param {React.ReactNode} props.children - Scene children
* @returns {JSX.Element}
*
* Notes:
* - Uses WebGPURenderer (three.js) for next-gen rendering
* - Handles color space and tone mapping for WebGPU
* - Preloads assets and adapts DPR
*/
const WebGPUScene = ({ debug = false, frameloop = 'always', orthographic = false, children, ...props }: SceneProps) => {
const [canvasFrameloop, setCanvasFrameloop] = useState<'always' | 'demand' | 'never'>('never')
return (
<Canvas
id='__webgpucanvas'
{...props}
frameloop={canvasFrameloop}
gl={async (props) => {
const renderer = new WebGPURenderer(props as any)
await renderer.init()
setCanvasFrameloop(frameloop)
return renderer
}}
>
<Preload all />
<AdaptiveDpr />
{children}
<ColorSpaceCorrection />
{debug ? <StatsGl className='fragments-supply__statsgl' /> : null}
<OrthographicCamera makeDefault position={[0, 0, 1]} />
</Canvas>
)
}
const App = () => {
const ref = useRef<any>(null)
return (
<section
style={{
width: '100vw',
height: '100vh',
}}
ref={ref}
>
<Suspense fallback={null}>
<WebGPUScene
style={{
position: 'fixed',
inset: 0,
pointerEvents: 'none',
}}
eventSource={ref}
eventPrefix='client'
></WebGPUScene>
</Suspense>
</section>
)
}
// Render the app
const rootElement = document.getElementById('root')!
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement)
root.render(
<StrictMode>
<App />
</StrictMode>,
)
}Using the Sketch Component
From here, you can add any components you want to your scene. Here we're going to use the WebGPUSketch component to render a full size sketch.
Here we're adding this into our App component.
/**
* WebGPU sketch mesh.
* @param {Object} props
* @param {NodeRepresentation} [props.colorNode] - Node for color, defaults to vec3(uv, sin(time)).
* @returns {JSX.Element}
*/
const WebGPUSketch = ({ colorNode }) => {
const s = new MeshBasicNodeMaterial({ transparent: true })
const _uv = uv()
const _colorNode = colorNode ? colorNode : vec3(_uv, sin(time))
s.colorNode = _colorNode
const { width, height } = useThree((state) => state.viewport)
return (
<mesh material={s} scale={[width, height, 1]}>
<planeGeometry args={[1, 1]} />
</mesh>
)
}
const App = () => {
const ref = useRef<any>(null)
const colorNode = Fn(() => vec3(uv(), oscSine(time.mul(0.5))))
return (
<section
style={{
width: '100vw',
height: '100vh',
}}
ref={ref}
>
<Suspense fallback={null}>
<WebGPUScene
style={{
position: 'fixed',
inset: 0,
pointerEvents: 'none',
}}
eventSource={ref}
eventPrefix='client'
>
<WebGPUSketch colorNode={colorNode()} />
</WebGPUScene>
</Suspense>
</section>
)
}Vanilla Javascript
If you have a vanilla javascript project, you can drop this entire file into your project to get a basic full screen WebGPU scene. If you're using the Fragments Vanilla Boilerplate Github Repo, this will already be set up with a basic router.
import {
WebGPURenderer,
MeshBasicNodeMaterial,
PlaneGeometry,
Scene,
Mesh,
OrthographicCamera,
DoubleSide,
NoToneMapping,
LinearSRGBColorSpace,
} from 'three/webgpu'
import { Fn, uv, sin, time, vec3 } from 'three/tsl'
// Canvas
const canvas = document.querySelector('#webgpu-canvas')
const scene = new Scene()
// Sketch geometry
const sketchGeometry = new PlaneGeometry(1, 1, 1, 1)
// Sketch material
const sketchMaterial = new MeshBasicNodeMaterial({
transparent: true,
side: DoubleSide,
depthWrite: false,
})
// Basic sketch node
const colorNode = Fn(() => vec3(uv(), sin(time.mul(0.5))))
sketchMaterial.colorNode = colorNode()
// Add a fullscreeen sketch plane to the scene
const sketch = new Mesh(sketchGeometry, sketchMaterial)
sketch.scale.set(2, 2, 1)
scene.add(sketch)
// Viewport sizes
const viewport = {
width: window.innerWidth,
height: window.innerHeight,
}
window.addEventListener('resize', () => {
// Update sizes
viewport.width = window.innerWidth
viewport.height = window.innerHeight
// Update camera
camera.aspect = viewport.width / viewport.height
camera.updateProjectionMatrix()
// Update renderer
renderer.setSize(viewport.width, viewport.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})
// Camera
const camera = new OrthographicCamera(-1, 1, 1, -1, 0.1, 100)
camera.position.z = 1
scene.add(camera)
// Renderer
const renderer = new WebGPURenderer({
canvas: canvas,
antialias: true,
toneMapping: NoToneMapping,
outputColorSpace: LinearSRGBColorSpace,
})
renderer.setSize(viewport.width, viewport.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
renderer.setClearColor('#000000')
const frame = () => {
renderer.renderAsync(scene, camera)
window.requestAnimationFrame(frame)
}
frame()























































































































