Custom JavaScript
Custom JavaScript lets you add interactive behavior to any element. Your code runs when the element mounts in the live preview and on the published site.
The element variable
Your code receives an element variable that references the DOM node of the selected element. Use it to manipulate the element directly.
element.addEventListener('click', () => {
element.style.backgroundColor = '#' + Math.floor(Math.random()*16777215).toString(16);
});The element is the actual DOM node, so you have full access to the DOM API — querySelector, getBoundingClientRect, classList, style, and everything else.
Importing libraries
You can import external libraries using dynamic import() from a CDN like esm.sh:
import('https://esm.sh/canvas-confetti').then(({ default: confetti }) => {
element.addEventListener('click', () => {
const rect = element.getBoundingClientRect();
confetti({
origin: {
x: (rect.left + rect.width / 2) / window.innerWidth,
y: (rect.top + rect.height / 2) / window.innerHeight,
}
});
});
});Libraries like Three.js, OGL, GSAP, and others work this way. The import is cached by the browser after the first load.
Lifecycle
Your code runs once when the element appears on the page. Use the Play button to preview it in the builder, and press Stop to end the preview.
Using @controls
The @controls system lets you define UI controls that inject variables into your code. Each control becomes a const declaration at the top of your code.
/** @controls {
speed: { type: "slider", label: "Speed", min: 10, max: 200, step: 10, default: 50 },
color: { type: "color", label: "Color", default: "#ff0000" },
loop: { type: "toggle", label: "Loop", default: true }
} */
// These variables are available because of @controls:
// const speed = 50;
// const color = "#ff0000";
// const loop = true;
element.style.transition = `all ${speed}ms ease`;
element.style.color = color;When you change a control value in the toolbar, the default value in the code is updated and the preview restarts with the new values.
See Controls for all available control types.
Examples
Parallax scroll effect
window.addEventListener('scroll', () => {
const rect = element.getBoundingClientRect();
const speed = 0.3;
const yOffset = (window.innerHeight / 2 - rect.top) * speed;
element.style.transform = `translateY(${yOffset}px)`;
});WebGL background with OGL
import('https://esm.sh/ogl').then(({ Renderer, Program, Mesh, Triangle }) => {
const renderer = new Renderer({ alpha: true });
const gl = renderer.gl;
gl.canvas.style.cssText = 'width:100%;height:100%;display:block';
element.appendChild(gl.canvas);
// ... shader setup and render loop
});Typed text effect
const text = element.textContent;
element.textContent = '';
let i = 0;
const interval = setInterval(() => {
if (i < text.length) {
element.textContent += text[i];
i++;
} else {
clearInterval(interval);
}
}, 50);Tips
- Use
element.clientWidthandelement.clientHeightfor sizing - You can append children, create canvases, and add event listeners to
element - Avoid modifying
element.style.positionorelement.style.width/heightas this may conflict with the builder layout