3D
FrameScript provides WebGL helpers and a Three.js canvas wrapper that survives context loss. Render mode waits for recovery so incomplete frames are avoided.
WebGL helpers
useWebGLContextinitializes WebGL, handleswebglcontextlost, and recreates resources.useWebGLFrameWaiterwaits for GPU completion per frame during headless rendering.
import { useRef } from "react"
import { useWebGLContext, useWebGLFrameWaiter } from "../src/lib/webgl"
const Canvas = () => {
const canvasRef = useRef<HTMLCanvasElement | null>(null)
const { glRef } = useWebGLContext(canvasRef, ({ gl }) => {
// init shaders/buffers
return () => {
// dispose resources
}
})
useWebGLFrameWaiter(glRef)
return <canvas ref={canvasRef} />
}
ThreeCanvas
<ThreeCanvas /> integrates with useAnimation by sampling variables in the update callback.
import { useAnimation, useVariable } from "../src/lib/animation"
import { BEZIER_SMOOTH } from "../src/lib/animation/functions"
import { seconds } from "../src/lib/frame"
import { ThreeCanvas, THREE, disposeThreeObject } from "../src/lib/webgl/three"
const Scene = () => {
const progress = useVariable(0)
useAnimation(async (ctx) => {
await ctx.move(progress).to(1, seconds(2), BEZIER_SMOOTH)
await ctx.move(progress).to(0, seconds(2), BEZIER_SMOOTH)
}, [])
return (
<ThreeCanvas
setup={({ renderer, size }) => {
renderer.outputColorSpace = THREE.SRGBColorSpace
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(45, size.cssWidth / size.cssHeight, 0.1, 100)
camera.position.z = 6
const mesh = new THREE.Mesh(
new THREE.BoxGeometry(),
new THREE.MeshStandardMaterial({ color: 0x44aa88 })
)
scene.add(mesh)
const light = new THREE.DirectionalLight(0xffffff, 1)
light.position.set(2, 3, 4)
scene.add(light)
return {
scene,
camera,
update: ({ frame }) => {
const t = progress.get(frame)
mesh.position.x = (t - 0.5) * 3
mesh.rotation.y = t * Math.PI * 2
},
dispose: () => disposeThreeObject(mesh),
}
}}
/>
)
}