Skip to main content
Version: Next 🚧

Canvas, React, Three.js, and web content

Veena's built-ins are conveniences. Custom content can use Canvas 2D, normal DOM/CSS, React, or Three.js as long as its picture is a deterministic function of sampled state and scene time.

Canvas 2D tattva​

CanvasTattva owns a real HTML <canvas> while Veena owns its outer placement, camera projection, opacity, layer, CSS, grouping, and timeline state. Its bitmap automatically matches the output resolution implied by its world-space size.

import { CanvasTattva } from "veena/adapters";
import type { TattvaState } from "veena/core";

interface WaveState extends TattvaState { phase: number }

const wave = new CanvasTattva<WaveState>({
draw({ canvas, context, theme }, state, sample) {
const { width, height } = canvas;
context.fillStyle = theme.colors.surface;
context.fillRect(0, 0, width, height);
context.strokeStyle = "#22d3ee";
context.beginPath();
for (let x = 0; x <= width; x += 2) {
const y = height / 2 + Math.sin(x / width * Math.PI * 4 + state.phase) * height * 0.25;
if (x === 0) context.moveTo(x, y);
else context.lineTo(x, y);
}
context.stroke();
context.fillText(`frame ${sample.frame}`, 16, 24);
},
}, {
size: [8, 4],
state: { phase: 0 },
});

draw receives the complete sampled state plus { time, fps, frame, duration, progress }. Veena resets the 2D context before each draw, so every frame must draw the complete picture and cannot accidentally depend on the previously rendered frame. Use canvas.width, canvas.height, or pixelsPerUnit for resolution-aware drawing. Animate custom state through an ordinary Timeline.

Canvas 3D / WebGL2 tattva​

Browsers do not expose a literal canvas.getContext("3d"); raw 3D canvas rendering uses WebGL or WebGPU. Canvas3DTattva provides a persistent WebGL2 context for custom shaders and buffers:

import { Canvas3DTattva } from "veena/adapters";

let program: WebGLProgram;

const surface = new Canvas3DTattva({
setup({ gl }) {
program = gl.createProgram()!;
// Compile shaders, link the program, and create persistent buffers here.
},
draw({ gl }, state, sample) {
gl.useProgram(program);
// Apply all sampled uniforms and draw the complete frame here.
},
}, { size: [8, 4] });

The runtime configures the viewport and clears color, depth, and stencil buffers before every draw. WebGL objects and state remain persistent, so draw must update every time-varying uniform and binding from state or sample. The context uses an alpha channel and preserves its drawing buffer for deterministic screenshots and exports.

Use Canvas3DTattva when you want raw WebGL2. Use ThreeTattva when you prefer Three.js scene, camera, material, and geometry abstractions.

React tattva​

import { ReactTattva, type TattvaState } from "veena/adapters";

interface CardState extends TattvaState { progress: number }

const card = new ReactTattva<CardState>(
(state, { theme }) => (
<div style={{
width: 420,
padding: 28,
borderRadius: theme.radii.large,
color: theme.colors.textPrimary,
background: theme.colors.surface,
}}>
Progress: {Math.round(state.progress * 100)}%
</div>
),
{ state: { progress: 0 } },
);

Inside nested React components, useVeenaTheme() reads the resolved scene or group theme.

Three tattva​

import { ThreeTattva } from "veena/adapters";
import { Mesh, MeshStandardMaterial, SphereGeometry } from "three";

const globe = new ThreeTattva({
setup({ scene }) {
scene.add(new Mesh(
new SphereGeometry(1, 48, 32),
new MeshStandardMaterial({ color: "#58c4dd" }),
));
},
update({ scene }, state) {
scene.rotation.y = state.rotationY * Math.PI / 180;
},
});

setup creates persistent Three.js resources. update applies sampled state for the exact frame.

Deterministic extension rules​

  • Do not use requestAnimationFrame, Date.now(), or random values without a fixed seed.
  • Do not fetch changing remote content during render.
  • Derive visual state from the provided state, theme, and scene time.
  • Load required assets before the first frame through Veena's asset paths or deterministic setup.

See examples/canvas-2d.ts, examples/canvas-3d.ts, examples/react-card.tsx, examples/three-camera.ts, and examples/textured-surface.ts.