跳到正文
cccskills
免费在 GitHub 公开

shader-web

Getting a shader onto a page - the WebGL bootstrap, uniform plumbing, device pixel ratio, resize, pause on hidden tab, reduced motion, the drop-in HTML file, parameter updates over postMessage, and embedding it without a framework. Read this when the shader has to run in a browser.

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md7.7 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Shaders on the web

shader_target target=web writes one self-contained HTML file: no framework, no build step, no request. That is the deployment model the Radiant corpus uses, and it is the right default because a page that needs nothing cannot break because something is missing.

The bootstrap, in full

Everything a fragment shader page needs, and nothing else:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shader</title>
<style>
  html, body { margin: 0; height: 100%; overflow: hidden; background: #0a0a0a; }
  canvas { display: block; width: 100vw; height: 100vh; }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl', { alpha: false, antialias: false });
if (!gl) throw new Error('no WebGL');

const VERTEX = 'attribute vec2 a_position; void main() { gl_Position = vec4(a_position, 0.0, 1.0); }';
const FRAGMENT = `precision highp float;
uniform vec2 u_resolution;
uniform float u_time;
void main() {
  vec2 uv = gl_FragCoord.xy / u_resolution;
  gl_FragColor = vec4(uv.x, uv.y, 0.5 + 0.5 * sin(u_time), 1.0);
}`;

function compile(type, source) {
  const shader = gl.createShader(type);
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(shader) || 'compile failed');
  }
  return shader;
}

const program = gl.createProgram();
gl.attachShader(program, compile(gl.VERTEX_SHADER, VERTEX));
gl.attachShader(program, compile(gl.FRAGMENT_SHADER, FRAGMENT));
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(program) || 'link failed');
}
gl.useProgram(program);

const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
const at = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(at);
gl.vertexAttribPointer(at, 2, gl.FLOAT, false, 0, 0);

const uTime = gl.getUniformLocation(program, 'u_time');
const uRes = gl.getUniformLocation(program, 'u_resolution');

function resize() {
  // Cap the pixel ratio: a 3x phone rendering a 4K shader is a battery fire.
  const dpr = Math.min(window.devicePixelRatio || 1, 2);
  canvas.width = Math.round(canvas.clientWidth * dpr);
  canvas.height = Math.round(canvas.clientHeight * dpr);
  gl.viewport(0, 0, canvas.width, canvas.height);
}
window.addEventListener('resize', resize);
resize();

let running = true;
const start = performance.now();
function frame(now) {
  if (!running) return;
  gl.uniform1f(uTime, (now - start) / 1000);
  gl.uniform2f(uRes, canvas.width, canvas.height);
  gl.drawArrays(gl.TRIANGLES, 0, 3);
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);

document.addEventListener('visibilitychange', () => {
  if (document.hidden) { running = false; }
  else if (!running) { running = true; requestAnimationFrame(frame); }
});
</script>
</body>
</html>

Every part of that is load-bearing:

  • preserveDrawingBuffer is not set here. Leave it off for animation (it costs performance); turn it ON if you need toDataURL() or a screenshot.
  • DPR capped at 2. canvas.width = clientWidth * devicePixelRatio uncapped renders four times the pixels on a 2x display for a difference nobody sees.
  • visibilitychange. A background tab that keeps drawing wastes a battery and is the most common complaint about shader backgrounds.
  • resize sets canvas.width, which resets the drawing buffer - so the viewport must be set again in the same handler, after the size.
  • The error is thrown with the log. A black canvas with nothing in the console is the worst outcome, and it is always avoidable.

Two contexts, two dialects

dialectcontextextra
es100 (gl_FragColor)getContext('webgl')call getExtension('OES_standard_derivatives') before compiling if you use fwidth
es300 (#version 300 es)getContext('webgl2')none
shadertoy / shader-skillsgetContext('webgl2')the page must supply iResolution, iTime, iMouse, iFrame

A WebGL 1 context rejects a #version 300 es shader with unsupported shader version 300 - which reads like a shader bug and is a context bug. shader_target target=web picks the right one for you.

Driving it from outside: the postMessage contract

A drop-in page has no API, so it takes parameter updates over postMessage. This is Radiant's own shape, and pages this row emits accept exactly it:

// on the parent page
const frame = document.querySelector('iframe');
frame.contentWindow.postMessage({ type: 'param', name: 'SPEED', value: 0.8 }, '*');

The page listens:

window.addEventListener('message', (event) => {
  const data = event.data;
  if (!data || data.type !== 'param') return;
  if (data.name === 'SPEED') speed = data.value;
});

Match on the parameter name (SPEED, as declared in // @param SPEED ...), not on the uniform name. The page owns the translation to u_speed.

Embedding it as a background

<iframe
  src="my-shader.html"
  title="Background"
  style="position: fixed; inset: 0; width: 100%; height: 100%; border: 0; z-index: -1;"
  aria-hidden="true"
  tabindex="-1"
></iframe>

Three habits: aria-hidden="true" (it is decoration, not content), z-index: -1 with the content above it, and no pointer events - an iframe over the page eats every click. If it must sit behind text, put it in a container with pointer-events: none.

Do not put a shader behind text without checking contrast: a moving background behind body copy is a readability failure waiting to happen. Either keep it behind a scrim (background: rgba(0,0,0,.55) over the canvas) or keep it in a hero band.

Inline instead of an iframe

If the page already has a canvas, the bootstrap above drops in directly: keep the program in a module, expose setParam(name, value) and resize(). The iframe is only needed when the shader is a file - which is the case for a drop-in background, and which is why the emitted artefact is a whole page.

Reduced motion is not optional

const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const time = reduced ? 0 : (now - start) / 1000;

An animation that ignores this preference is an accessibility defect, not a feature. The page this row emits freezes time at 0 when the preference is set, so the shader still shows its picture, unmoving.

What to check before you ship it

  1. It renders at the size it will be used at - not at 800x600.
  2. No console output. Warnings are still read as errors by people who look.
  3. It stops when the tab is hidden (watch requestAnimationFrame in the profiler).
  4. It survives a resize and a device rotation.
  5. It costs what you think: on a 4K display a heavy raymarcher is 8.3M pixels per frame. shader_render at your intended size is the honest measurement.
  6. Nothing reaches the network. A shader needs no font, no image, no CDN.

shader_verify proves the GLSL; the page is where the driver, the DPR and the resize behaviour get their say. Test it in the browser you are shipping to.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Author an editorial HTML+SVG diagram with the Diagram Design kit - choose the visual type, read its reference, start from an example, and satisfy the safety and accessibility contract that diagram_write({kind:"html"}) enforces.

中文简介正在准备中。当前显示原文说明。

vecnode/vn-code102026年10月20日 更新

Control this machine's Docker or Podman engine through the docker_* tools - read the engine's state first, list containers and images, inspect one, tail its logs, pull an image and start it, then stop, restart or remove what you started. Covers the engine that is asleep behind Docker Desktop and the exact call for each verb, the confirm rule removals obey, and the flags the tools deliberately do not have.

中文简介正在准备中。当前显示原文说明。

vecnode/vn-code102026年10月20日 更新

Drive ffmpeg through the media_run tool as an argv array - remux instead of re-encode, map streams explicitly, trim at keyframes, scale and crop, extract audio and frames, and build GIFs, contact sheets and concatenations. Covers the flags this pack injects, reading a failure from its exit code and last log lines, and the version-dependent spellings to hedge.

中文简介正在准备中。当前显示原文说明。

vecnode/vn-code102026年10月20日 更新

Read a media file's real structure with ffprobe through media_run - the JSON invocation and how to read it, what every family of field means, how to count frames, sample without decoding everything, detect HDR and interlacing, and diagnose a missing duration, a stream with no frames or a container that lies. Covers when media_probe's own report is already enough.

中文简介正在准备中。当前显示原文说明。

vecnode/vn-code102026年10月20日 更新

Writing a fragment shader that looks like something - the full-screen triangle, uv space, signed distance fields, noise and fbm, domain warping, raymarching budgets, tone mapping and banding. Every example compiles. Read this when the task is "make it look good", not "make it compile".

中文简介正在准备中。当前显示原文说明。

vecnode/vn-code102026年10月20日 更新

Use when the user wants to design, redesign, shape, critique, audit, polish, simplify, harden, animate, or otherwise improve a frontend interface: websites, landing pages, dashboards, product UI, components, forms, settings, onboarding, empty states, and native iOS or Android screens. Handles UX review, hierarchy, layout, typography, color, motion, UX copy, accessibility, responsive behavior, edge cases, and capturing or documenting design systems (DESIGN.md, tokens). Also for making bland UI bolder, loud UI quieter, or ambitious visual effects. Not for backend-only or non-UI tasks.

中文简介正在准备中。当前显示原文说明。

vecnode/vn-code102026年10月20日 更新

查看 vecnode 的全部技能

报告此技能的问题