specimen
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSpecimen: make an icon
Specimen:制作图标
You are making one Specimen icon: a single glossy object on a soft rounded tile, lit like a piece in a display case. The colour arrives only as light. It loops without a jump. From one piece file it ships in a dark and a light theme, as path traced video or as a live raster element.
The pieces in are the bar. Read the one closest to the request before you write anything. If what you make would look out of place next to them, it is not done.
examples/You write one thing: the piece file, . The tile, the studio, the lights, the post, the themes, the path tracer, the denoiser and the encode are fixed and live in . Never edit , never paste a changed copy of it, never re-implement what it already does. If you think the runtime is missing something, tell the person instead of working around it.
<name>.specimen.jsruntime/runtime/Every file named below is in this skill's folder. The piece is written in the person's working directory (or where they say); media goes where they say (, …) or beside the piece, prefixed with the icon's name. Never create a folder of your own in their project. Run the scripts from there, by their path in this folder.
public/icons/Specimen is only this skill. There is no npm package and no install step. Never run , never add a dependency to the person's project, never import in the piece: it gets everything through .
npm installthreectx你正在制作一个 Specimen 图标:一个置于柔和圆角 tile 上的单一光泽对象,打光方式如同陈列柜中的展品。颜色只以光的形式呈现。动画循环时不会发生跳变。只需一个 piece 文件,即可交付包含深色和浅色主题的版本,形式可以是 path tracing 视频,也可以是 live raster 元素。
examples/你只需要编写一个文件:piece 文件 。tile、studio、灯光、后期处理、主题、path tracer、降噪器和编码流程都是固定的,位于 中。绝不编辑 ,绝不粘贴修改后的副本,也绝不重新实现其中已有的功能。如果你认为 runtime 缺少某项能力,应告知对方,而不是自行绕过。
<name>.specimen.jsruntime/runtime/下文提到的每个文件都位于此 skill 的文件夹中。piece 写在对方的工作目录中(或对方指定的位置);媒体文件写入对方指定的位置(如 ),或者放在 piece 旁边,并以图标名称作为前缀。不要在对方项目中创建自己的文件夹。应从该目录运行脚本,并通过此文件夹中的路径调用脚本。
public/icons/Specimen 仅指这个 skill。不存在 npm package,也没有安装步骤。绝不运行 ,绝不向对方项目添加依赖,也绝不在 piece 中导入 ;所有能力都通过 提供。
npm installthreectxThe skill folder
skill 文件夹
specimen/
SKILL.md this file
concepts.md objects that read well, metaphors by product area, what failed
package.json { "type": "module" }, so Node reads this folder's scripts the same under any project
render.mjs piece → webm + mp4 + poster per theme (video), or runtime + piece + posters (--live)
sheet.mjs contact sheet: raster frames, a dark row and a light row per piece; --pt, --guide
validate.mjs the measurable rules (section 5)
piece.mjs reads a piece in Node: its name, its syntax errors with the line, the contract
session.mjs opens a piece on the runtime page in headless Chrome
cdp.mjs Chrome finder, a minimal DevTools Protocol client and a static server
runtime/ fixed — never edited
specimen.js the bundle: three (WebGPU), the path tracer, the denoiser, the stage, <specimen-icon>
weights.tza the denoiser's weights
THIRD_PARTY_NOTICES.md the bundled packages' and the weights' licences; --live ships it as specimen.js.LICENSE.txt
page.html the page render, sheet and validate load a piece into
examples/ the reference pieces (section 7)The scripts run with plain (22 or newer) and use only Node built-ins. They drive the Chrome already on the machine (or ), and video needs . If one is missing, the script names it: tell the person exactly what is missing and how to get it (); don't install anything on your own.
nodeSPECIMEN_CHROMEffmpegbrew install ffmpegspecimen/
SKILL.md this file
concepts.md objects that read well, metaphors by product area, what failed
package.json { "type": "module" }, so Node reads this folder's scripts the same under any project
render.mjs piece → webm + mp4 + poster per theme (video), or runtime + piece + posters (--live)
sheet.mjs contact sheet: raster frames, a dark row and a light row per piece; --pt, --guide
validate.mjs the measurable rules (section 5)
piece.mjs reads a piece in Node: its name, its syntax errors with the line, the contract
session.mjs opens a piece on the runtime page in headless Chrome
cdp.mjs Chrome finder, a minimal DevTools Protocol client and a static server
runtime/ fixed — never edited
specimen.js the bundle: three (WebGPU), the path tracer, the denoiser, the stage, <specimen-icon>
weights.tza the denoiser's weights
THIRD_PARTY_NOTICES.md the bundled packages' and the weights' licences; --live ships it as specimen.js.LICENSE.txt
page.html the page render, sheet and validate load a piece into
examples/ the reference pieces (section 7)这些脚本使用纯 (22 或更高版本)运行,并且只使用 Node 内置模块。它们会调用机器上已有的 Chrome(或 ),视频渲染还需要 。如果缺少其中任何一项,脚本会明确指出:请准确告知对方缺少什么以及如何获取(例如 ),不要自行安装任何东西。
nodeSPECIMEN_CHROMEffmpegbrew install ffmpeg0. Delivery
0. 交付
Ask this first, in one message, before any concept:
Video (default) — path traced,and<name>-dark.webm/.mp4with a poster each, and a<name>-light.webm/.mp4snippet. The best look, a few hundred KB per theme, plays everywhere. Live —<video>(with its licence file) and the piece copied into their static folder, a poster per theme, and aspecimen.jssnippet. It is raster, not path traced: softer reflections, no denoiser. Say so plainly. It needs WebGPU and falls back to the poster without it or with reduced motion. Both — video by default, live on top only where they turn it on.<specimen-icon>
Both themes are rendered unless the person asks for one. Also ask where the files go; if they don't say, put them beside the piece. If there is nobody to ask, take video, both themes, beside the piece, and say so.
在提出任何概念之前,先在一条消息中询问:
Video(默认)——采用 path tracing,输出和<name>-dark.webm/.mp4,每个主题各附一张 poster,并提供<name>-light.webm/.mp4代码片段。效果最佳,每个主题通常只有数百 KB,可在各种环境中播放。 Live——提供<video>(及其 licence 文件),并将 piece 复制到对方的静态资源文件夹;每个主题各有一张 poster,并提供specimen.js代码片段。它采用 raster,而非 path tracing:反射更柔和,且没有 denoiser。请如实说明这一点。它需要 WebGPU;如果没有 WebGPU 或用户启用了 reduced motion,则回退为 poster。 Both——默认使用 video,仅在对方启用 live 的位置额外叠加 live 元素。<specimen-icon>
除非对方要求只输出一个主题,否则深色和浅色主题都要渲染。还要询问文件应放在哪里;如果对方没有指定,就放在 piece 旁边。如果无法询问,则选择 video、两个主题,并放在 piece 旁边,同时明确说明。
Size
尺寸
Render at the size the icon is shown at: a video stretched past its pixels goes soft at the edges, and no encode setting gets them back. Find the width first in the project, in the CSS or component the icon goes into, at its widest breakpoint. Ask only when you can't tell, in the same message as the delivery:
| shown at (CSS px) | | video | per theme |
|---|---|---|---|
| up to 160 | 384 | 320 px | ~4 min |
| up to 320 | 768 | 640 px | ~17 min |
| up to 480 | 1152 | 960 px | ~36 min |
The video has to be at least twice the CSS width for retina screens, and it comes out at 5/6 of : take the first row that covers the width. Wider than 480: is 2.4 × the width rounded up to a multiple of 12, and the time grows with its square. For live, the size only sets the posters. If there is nobody to ask and nothing to read, take 768 and say so.
--size--size按照图标实际展示的尺寸进行渲染:视频如果被拉伸到超过自身像素尺寸,边缘就会变软,而且任何编码参数都无法恢复清晰度。先在项目中查找图标的显示宽度,在 CSS 或承载该图标的组件中,以最宽断点为准。只有在无法判断时才询问,并且要在同一消息中询问交付方式:
| 显示尺寸(CSS px) | | 视频 | 每个主题 |
|---|---|---|---|
| 最大 160 | 384 | 320 px | 约 4 分钟 |
| 最大 320 | 768 | 640 px | 约 17 分钟 |
| 最大 480 | 1152 | 960 px | 约 36 分钟 |
视频像素宽度至少要是 CSS 宽度的两倍,以适配 retina 屏幕。最终尺寸为 的 5/6:选择表格中第一行能够覆盖目标宽度的配置。宽度超过 480 px 时, 为目标宽度的 2.4 倍,并向上取整为 12 的倍数;耗时随其平方增长。对于 live,尺寸只决定 poster 的尺寸。如果无法询问且项目中也没有可读取的信息,则使用 768,并说明这一点。
--size--size1. Concept
1. 概念
Read . Offer two or three concepts, one line each:
concepts.mdName. The object · what moves · where the accent lives · the finishes.
Example:
Bolt. A thick soft-bevelled lightning bolt · a charge of light runs down through it twice per loop and pools in the tip · the charge and the pool under the tile · gloss body.
Then suggest two or three palettes for the accent, the one colour the icon carries, and let the person pick or name their own:
Palette. Cyan(a cool, technical read) · amber#22d3ee(warm, an alert) · neutral#f59e0b(no colour at all: the object reads by its finishes alone).#a1a1aa
The colour is the person's call: their brand, their section, their grid. Suggest from the product area (security reads cool, alerts warm, a monochrome grid neutral) and from the icons already beside it on the page; never pick one silently. The palette sets and, if they want a different emission, .
scene.accentscene.glowWait for the person to pick. Skip this only when they arrived with the object and the motion already chosen. If there is nobody to ask, take the concept with the clearest silhouette and say which one you took.
What makes a concept good:
- One object, one idea. A concept that needs a caption is not one yet.
- Silhouette first. Squint: if the outline alone doesn't say what it is, change the object.
- The object means something in the product. Auth gets a lock, alerts a bell. See .
concepts.md - One motion that tells the story, small and slow: a turn, a swing, a charge, a pulse. Not three things at once.
阅读 。提出两到三个概念,每个概念用一行描述:
concepts.md名称。 对象 · 运动方式 · 强调色出现的位置 · 表面 finish。
示例:
闪电。 一枚粗壮、圆润倒角的闪电 · 每轮循环中有两股光脉冲沿着它向下奔涌,并在尖端汇聚 · 强调色与光池落在 tile 下方 · 光泽材质。
然后建议两到三种强调色 palette,即图标承载的唯一一种颜色,让对方选择或指定自己的颜色:
Palette。 青色(冷色调,具有技术感)· 琥珀色#22d3ee(暖色调,适合告警)· 中性色#f59e0b(完全不使用颜色:对象仅依靠 finish 来呈现)。#a1a1aa
颜色由对方决定:可以对应其品牌、页面板块或图标网格。根据产品领域进行建议——安全类通常适合冷色,告警类适合暖色,单色网格适合中性色——同时参考页面上已有的相邻图标;绝不默默替对方选择。palette 会设置 ;如果对方希望使用不同的自发光颜色,还会设置 。
scene.accentscene.glow等待对方选择。只有当对方已经同时确定了对象和运动方式时,才可以跳过这一步。如果无法询问,则选择轮廓最清晰的方案,并说明采用了哪一个。
一个好的概念应具备以下特点:
- 一个对象,一个想法。 如果一个概念需要文字说明才能理解,就还不算完整。
- 轮廓优先。 眯起眼睛看:如果仅凭外轮廓无法辨认对象,就更换对象。
- 对象要在产品中表达明确含义。 Auth 使用锁,告警使用铃铛。参见 。
concepts.md - **用一种运动讲述故事,**动作要小且缓慢:旋转、摆动、充能或脉动。不要同时做三件事。
2. Read the closest example
2. 阅读最接近的示例
Open the example in nearest to the concept and read it whole. Copy its structure: the block, how it builds geometry with , how its keeps every term periodic.
examples/scenegeo.*update(phase)打开 中与概念最接近的示例,并完整阅读。复制它的结构: block、使用 构建几何体的方式,以及 如何确保每个动画项都保持周期性。
examples/scenegeo.*update(phase)3. Write the piece
3. 编写 piece
An ES module with two exports and no imports:
js
export const scene = {
accent: '#38bdf8', // the piece's only colour, always as light (a Tailwind 500 reads well)
glow: '#7dd3fc', // optional: the colour of its emission; default is the accent a step lighter
duration: 8, // seconds, 4–12; the loop must not jump
fit: { scale: 0.9, x: 0, y: 0 },// optical fit into the 58% safe area (section 4)
lights: { tint: 0.45 }, // optional: accent, rim, under, underFill, floor, tint, strips
};
export function object( ctx ) {
// build with ctx.mat.*, ctx.geo.*, ctx.texture()
return { mesh, update( phase ) { /* phase runs 0 → 1 once per loop */ } };
}这是一个仅包含两个导出、没有任何 import 的 ES module:
js
export const scene = {
accent: '#38bdf8', // the piece's only colour, always as light (a Tailwind 500 reads well)
glow: '#7dd3fc', // optional: the colour of its emission; default is the accent a step lighter
duration: 8, // seconds, 4–12; the loop must not jump
fit: { scale: 0.9, x: 0, y: 0 },// optical fit into the 58% safe area (section 4)
lights: { tint: 0.45 }, // optional: accent, rim, under, underFill, floor, tint, strips
};
export function object( ctx ) {
// build with ctx.mat.*, ctx.geo.*, ctx.texture()
return { mesh, update( phase ) { /* phase runs 0 → 1 once per loop */ } };
}ctx
ctxctx
ctx| Key | What it is |
|---|---|
| the bundle's three instance; the piece never imports three |
| the material roles below, resolved by the current theme |
| a rounded rectangle on a |
| a shape extruded with soft rounded edges, centred on z = 0 |
| a profile of |
| uv 0..1 across |
| the one dynamic texture: |
| a static vertical gradient, stops |
| the two colours as |
| |
| animation helpers: |
| 键 | 含义 |
|---|---|
| bundle 中的 three 实例;piece 绝不导入 three |
| 下文所述的材质角色,会根据当前主题解析 |
| 在 |
| 将形状挤出为边缘柔和圆润的实体,并以 z = 0 为中心 |
| 将由 |
| 在 |
| 唯一的动态纹理: |
| 静态垂直渐变; |
| 以 |
| |
| 动画辅助函数: |
Space and size
空间与尺寸
The tile is 2 × 2 units, centred on the origin, facing the camera (+x right, +y up, +z towards the camera). The safe box is 1.16 units square (58% of the tile), centred at y = +0.04. Build the object about 1.0–1.3 units wide and at most ~1.6 tall, centred on its own origin, at : the examples are this size at 0.8–0.9.
z ≈ 0.45fit.scalescene.fitscalexyy: -0.08geometry.center()fitThickness that reads at 64 px: tubes and rods of radius 0.12 or more, bevels of 0.05 or more. The key light is a soft box above and to the right, so a negative (around −0.3, which turns the object's right side towards the camera) catches it on the bevels.
rotation.ytile 大小为 2 × 2 个单位,以原点为中心,正对 camera(+x 向右,+y 向上,+z 朝向 camera)。safe box 为边长 1.16 个单位的正方形,即 tile 的 58%,中心位于 y = +0.04。将对象构建为宽约 1.0–1.3 个单位、最高约 1.6 个单位,以自身原点为中心,并放置在 z ≈ 0.45:示例在 为 0.8–0.9 时采用的就是这一尺寸。
fit.scalescene.fitscalexyy: -0.08geometry.center()fit在 64 px 下仍应清晰可读的厚度:管状物和杆状物的半径至少为 0.12,倒角至少为 0.05。主光源位于右上方,是一块柔和的 soft box,因此负的 (约 −0.3,使对象右侧转向 camera)会让倒角捕捉到主光。
rotation.yThe dynamic texture
动态纹理
draw(fn)fn(u, v)uvv = 0geo.planarUV(geometry, w, h)v = 0u = 0TubeGeometryuemissiveIntensityglow.r * k, glow.g * k, glow.b * kkk ≈ 0texture(256, 16)128 × 128drawdraw(fn)fn(u, v)uvv = 0geo.planarUV(geometry, w, h)v = 0u = 0TubeGeometryuemissiveIntensityglow.r * k, glow.g * k, glow.b * ktexture(256, 16)128 × 128drawMaterial roles
材质角色
Every material comes from . Never construct one ( fails validation): a hand-made material has one colour, and the light theme needs another.
mat.*new THREE.MeshStandardMaterial(…)| Role | Dark | Light | Use it for |
|---|---|---|---|
| glossy black | white glazed ceramic | the object's mass |
| chrome | chrome | rings, bands, hinges, the shackle |
| dark glass | clear glass over a white core | dies, lenses, screens |
| black a step lighter | pearl grey | raised details on the body |
| emission in the glow colour | the glow as surface colour, faint emission | badges, keyholes, a tip bead: one small lit part, never the mass |
| warm white glow | warm, faint glow | windows, portholes, flames |
ofinishfinishRepeatroughnessmetalnessclearcoatnormalMapsidetransparentdepthWriteemissiveMapalphaMapemissiveIntensitycoloremissiveemissiveMapdrawmat.glow所有材质都来自 。绝不自行创建材质( 会导致校验失败):手动创建的材质只有一种颜色,而浅色主题需要另一种颜色。
mat.*new THREE.MeshStandardMaterial(…)| 角色 | 深色主题 | 浅色主题 | 用途 |
|---|---|---|---|
| 光泽黑色 | 白色釉面陶瓷 | 对象主体 |
| 镀铬 | 镀铬 | 环、箍、铰链、锁梁 |
| 深色玻璃 | 透明玻璃覆盖白色核心 | 骰子、镜头、屏幕 |
| 略浅的黑色 | 珍珠灰 | 主体上的凸起细节 |
| 使用 glow 色发光 | glow 作为表面色,并带微弱自发光 | 徽章、锁孔、尖端珠饰:一个小面积的发光部件,绝不能成为主体 |
| 暖白色辉光 | 微弱的暖色辉光 | 窗户、舷窗、火焰 |
ofinishfinishRepeatroughnessmetalnessclearcoatnormalMapsidetransparentdepthWriteemissiveMapalphaMapemissiveIntensitycoloremissiveemissiveMapdrawmat.glowFinishes
Finish
Every role takes a surface finish: . The finish sets the physical values (and, for the textured ones, a fine static map) and keeps the role's colour, so it works in both themes. Values the piece passes itself still win over the finish.
mat.body({ finish: 'brushed', finishRepeat: 5 })| Finish | What the light does on it | Good on |
|---|---|---|
| a mirror under clearcoat: sharp reflections of the studio strips | bodies that should read as lacquer |
| no reflection, only soft form shading | skirts, cores, parts that must sit back |
| metal with fine lines: the highlight stretches into a streak across them | plates, frames, collars, a lock's body |
| metal with a grid of round holes, each catching its own glint | grilles, keycap plates, speaker faces |
| a rough field with sparse bright glints, like stone or sparkle paint | caps, keytops, a bell's body |
finishRepeatThe two thin strips that draw those crisp lines can be scaled with (0–1, default 1). Turn them down when a fine bevel sweeps through one as the object turns: the reflection is thinner than a pixel and flickers on and off across frames. The bolt uses .
scene.lights.stripsstrips: 0validate.mjs…Material(materials每个角色都可以指定表面 finish,例如 。finish 决定物理属性;对于带纹理的 finish,还会生成精细的静态贴图,同时保留角色原有的颜色,因此可以同时适用于两个主题。piece 自身传入的值仍优先于 finish。
mat.body({ finish: 'brushed', finishRepeat: 5 })| Finish | 光线在其上的表现 | 适合用于 |
|---|---|---|
| clearcoat 下呈镜面效果,可清晰反射 studio 条形灯 | 需要呈现漆面质感的物体 |
| 不产生反射,仅呈现柔和的形体明暗 | 裙边、核心,以及需要后退到背景中的部件 |
| 带细密纹路的金属,高光会横跨表面形成拉长的光 streak | 板件、框架、套环、锁体 |
| 带圆孔网格的金属,每个孔都会捕捉独立的高光 | 格栅、键帽底板、扬声器面板 |
| 粗糙表面散布少量明亮闪光,类似石材或闪粉涂料 | 盖子、键帽顶部、铃铛主体 |
finishRepeat用于绘制清晰线条的两条细窄条形灯可以通过 (0–1,默认 1)进行缩放。当细倒角在对象旋转时扫过条形灯,应将其调低:反射可能细于一个像素,并会在不同帧中忽明忽暗。闪电使用了 。
scene.lights.stripsstrips: 0validate.mjs…Material(materialsThe craft rules
工艺规则
- One object, one idea. No second object, no props, no text.
- Silhouette first. It reads at 64 px or it doesn't read.
- Polished light on every icon. The studio has broad soft boxes and two thin hard strips: the strips draw crisp lines of light along bevels and edges. Give the icon at least two finishes so those lines change character across it (a gloss top over a matte skirt, a brushed frame around a speckled body). The icon should read by how its surfaces take the light, not only by its outline.
- The accent only as light. The body stays black (dark) or white (light). The accent appears as glow, as the pool under the tile and on the rim. Never paint a surface the accent colour.
- Fit in the 58% box. The solid mass sits in the safe area, centred a touch above the middle, and never comes within 4% of the tile edge. Use , not the object's own position, to place it.
scene.fit - Loop without a jump. Every term of is a function of
updatewith integer harmonics, or aphase · TAUenvelope that is 0 at both ends of the loop. Phase 1 must look exactly like phase 0.smoothstep - Light stays in the tile. Glow and halos that spill past the tile edge fail validation.
- Never edit .
runtime/
- 一个对象,一个想法。 不添加第二个对象、道具或文字。
- 轮廓优先。 如果在 64 px 下无法辨认,就没有通过。
- 每个图标都要有精致的光照。 studio 包含宽大的 soft box 和两条细窄的 hard strip:这些条形灯会沿倒角和边缘绘制清晰的光线线条。图标至少应使用两种 finish,让这些光线线条在不同表面上呈现不同质感,例如光泽顶部搭配 matte 裙边,或 brushed 框架围绕 speckled 主体。图标应通过表面如何承接光线来呈现,而不只是依靠轮廓。
- 强调色只以光的形式出现。 主体在深色主题中保持黑色,在浅色主题中保持白色。强调色只表现为辉光、tile 下方的光池以及边缘光。绝不能用强调色涂绘表面。
- 适配 58% safe box。 实体主体位于安全区域内,略高于中心,且绝不能进入 tile 边缘 4% 以内。使用 放置对象,而不是修改对象自身的位置。
scene.fit - 循环时不跳变。 中的每一项都必须是
update的函数,并使用整数谐波;或者使用在循环首尾均为 0 的phase · TAUenvelope。phase 1 必须与 phase 0 完全一致。smoothstep - 光线始终留在 tile 内。 溢出 tile 边缘的辉光和光晕会导致校验失败。
- 绝不编辑 。
runtime/
4. Sheet → read → fix
4. Sheet → 读取 → 修正
node <skill>/sheet.mjs <piece...> [--frames 0,60,120,180] [--guide] [--pt] [--out sheet.png]It renders raster frames 0, 60, 120 and 180 (or ) as 256 px cells, a dark row and a light row per piece, with drawing the 58% safe box in violet. adds a fifth cell to each row: frame 0 path traced and denoised. A raster run takes a few seconds per piece; adds about half a minute per piece. Pass an example after your piece to compare them row by row.
--frames--guide--pt--ptRead the PNG. Check: the silhouette reads, the mass sits in the box in every frame, the light row reads as well as the dark one (glow visible on white, nothing grey and muddy), the motion is visible across the frames. Fix and repeat until it would sit beside the examples. Run at least once before the full render: the raster cells are brighter than the final, and a black part that reads in raster can sink into the dark tile once path traced. To inspect a detail at 256 px, sheet a throwaway copy with a larger in the scratch directory, never in the person's project.
--ptfit.scalenode <skill>/sheet.mjs <piece...> [--frames 0,60,120,180] [--guide] [--pt] [--out sheet.png]它会将 raster 帧 0、60、120 和 180(或 指定的帧)渲染为 256 px 单元格;每个 piece 包含一行深色主题和一行浅色主题。 会以紫色绘制 58% safe box。 会为每行增加第五个单元格:帧 0 的 path tracing 与降噪结果。一次 raster 渲染每个 piece 需要几秒钟;使用 时,每个 piece 约增加半分钟。可以将一个示例放在 piece 后面,以便逐行对比。
--frames--guide--pt--pt读取 PNG。检查:轮廓是否清晰可辨;每一帧中主体是否都位于 safe box 内;浅色行是否与深色行一样清晰(在白色背景上应能看到辉光,且不能出现灰色、发浑的效果);跨帧观察时运动是否清楚。修正并重复,直到作品能够与示例并列展示。完整渲染前至少运行一次 :raster 单元格比最终效果更亮,因此在 raster 中能够看清的黑色部件,经过 path tracing 后可能会沉入深色 tile。若要在 256 px 下检查细节,可在 scratch 目录中生成一份 更大的临时副本并制作 sheet,绝不要在对方项目中这样做。
--ptfit.scale5. Validate
5. 校验
node <skill>/validate.mjs <name>.specimen.jsIt prints each failed rule with its reason and a number, and exits 1 on any failure. Repeat until it prints .
ok| Rule | What fails it |
|---|---|
| no |
| a |
| raster phase 0 against phase 1 differs by more than 0.5 on average (0–255) |
| the solid mass (the middle 90% of the object's silhouette, with transparent meshes such as flames and halos left out; opaque |
| anything outside the tile is more than 2/255 brighter than the page |
| |
node <skill>/validate.mjs <name>.specimen.js它会打印每一条失败规则及其原因和编号;只要存在失败项,就会以状态码 1 退出。重复执行,直到输出 。
ok| 规则 | 失败条件 |
|---|---|
| 没有导出 |
| 源代码中出现 |
| raster 的 phase 0 与 phase 1 平均差异超过 0.5(范围为 0–255) |
| 在循环的 0、¼、½ 和 ¾ 帧中,实体主体(对象轮廓中间 90% 的区域, flames 和 halos 等透明 mesh 不计入,但 opaque 的 |
| tile 外的任何区域比页面背景亮出超过 2/255 |
| 调用了两次 |
6. Render and hand over
6. 渲染与交付
Announce the time before you start, from the size table in §0: at it is about 4 s a frame on a recent Mac, so an 8 s loop (240 frames) is about 17 minutes per theme, about 35 for both. Live alone renders only the two posters, a minute or so. A video render outlasts any foreground command timeout, so run it in the background and watch its progress lines (, every 30 frames) until it prints the files; never run it in the foreground, where a timeout kills it halfway.
--size 76830/240 · 666 s leftnode <skill>/render.mjs <name>.specimen.js --out public/icons --size 768 # video, both themes
node <skill>/render.mjs <name>.specimen.js --out public/icons --live # live
node <skill>/render.mjs <name>.specimen.js --out public/icons --live --video # both--themes dark/<folder name>Hand over the files and the snippet, then ask whether the site follows the OS theme or has a toggle (). The printed video snippet follows the OS:
[data-theme]html
<video autoplay muted loop playsinline poster="/icons/bolt-dark-poster.jpg">
<source src="/icons/bolt-light.webm" type="video/webm" media="(prefers-color-scheme: light)">
<source src="/icons/bolt-light.mp4" type="video/mp4" media="(prefers-color-scheme: light)">
<source src="/icons/bolt-dark.webm" type="video/webm">
<source src="/icons/bolt-dark.mp4" type="video/mp4">
</video>For a site with a toggle, give two videos and the CSS that picks one:
[data-theme]html
<video class="specimen-dark" autoplay muted loop playsinline poster="/icons/bolt-dark-poster.jpg">
<source src="/icons/bolt-dark.webm" type="video/webm">
<source src="/icons/bolt-dark.mp4" type="video/mp4">
</video>
<video class="specimen-light" autoplay muted loop playsinline poster="/icons/bolt-light-poster.jpg">
<source src="/icons/bolt-light.webm" type="video/webm">
<source src="/icons/bolt-light.mp4" type="video/mp4">
</video>
<style>
.specimen-light { display: none; }
[data-theme="light"] .specimen-light { display: block; }
[data-theme="light"] .specimen-dark { display: none; }
</style>Live:
html
<specimen-icon src="/icons/bolt.specimen.js" poster="/icons/bolt-dark-poster.jpg" poster-light="/icons/bolt-light-poster.jpg"></specimen-icon>
<script type="module" src="/icons/specimen.js"></script><specimen-icon>prefers-color-schemetheme="dark|light"[data-theme]poster-lightposterwidthOutside the tile every frame is exactly the page colour (black or white). On a section of another colour, blend it in: for dark, for light.
mix-blend-mode: screenmultiply开始前,根据 §0 的尺寸表说明预计耗时:在 下,近年款 Mac 每帧约需 4 秒,因此一个 8 秒循环(240 帧)每个主题约需 17 分钟,两个主题合计约 35 分钟。仅使用 live 时只会渲染两张 poster,大约一分钟即可完成。视频渲染时间可能超过任何前台命令的 timeout,因此应在后台运行,并持续观察进度行(例如 ,每 30 帧输出一次),直到脚本打印出文件;绝不要在前台运行,否则 timeout 会在渲染途中将其终止。
--size 76830/240 · 666 s leftnode <skill>/render.mjs <name>.specimen.js --out public/icons --size 768 # video, both themes
node <skill>/render.mjs <name>.specimen.js --out public/icons --live # live
node <skill>/render.mjs <name>.specimen.js --out public/icons --live --video # both--themes dark/<folder name>交付文件和代码片段,然后询问网站是跟随 OS 主题,还是提供主题切换按钮()。打印出的 video 代码片段会跟随 OS 主题:
[data-theme]html
<video autoplay muted loop playsinline poster="/icons/bolt-dark-poster.jpg">
<source src="/icons/bolt-light.webm" type="video/webm" media="(prefers-color-scheme: light)">
<source src="/icons/bolt-light.mp4" type="video/mp4" media="(prefers-color-scheme: light)">
<source src="/icons/bolt-dark.webm" type="video/webm">
<source src="/icons/bolt-dark.mp4" type="video/mp4">
</video>对于带 切换功能的网站,提供两段视频以及用于选择主题的 CSS:
[data-theme]html
<video class="specimen-dark" autoplay muted loop playsinline poster="/icons/bolt-dark-poster.jpg">
<source src="/icons/bolt-dark.webm" type="video/webm">
<source src="/icons/bolt-dark.mp4" type="video/mp4">
</video>
<video class="specimen-light" autoplay muted loop playsinline poster="/icons/bolt-light-poster.jpg">
<source src="/icons/bolt-light.webm" type="video/webm">
<source src="/icons/bolt-light.mp4" type="video/mp4">
</video>
<style>
.specimen-light { display: none; }
[data-theme="light"] .specimen-light { display: block; }
[data-theme="light"] .specimen-dark { display: none; }
</style>Live:
html
<specimen-icon src="/icons/bolt.specimen.js" poster="/icons/bolt-dark-poster.jpg" poster-light="/icons/bolt-light-poster.jpg"></specimen-icon>
<script type="module" src="/icons/specimen.js"></script>除非设置 (在带 的网站中,应由主题切换按钮设置),否则 会跟随 。当它离开可视区域时会暂停;如果缺少 WebGPU 或启用了 reduced motion,则保留 poster:浅色主题使用 ,其他情况使用 。通过 CSS 设置尺寸(始终保持正方形;默认宽度为 160 px)。
theme="dark|light"[data-theme]<specimen-icon>prefers-color-schemeposter-lightposterwidth每一帧中,tile 外的区域都精确使用页面颜色(黑色或白色)。如果所在 section 使用其他颜色,则进行混合:深色主题使用 ,浅色主题使用 。
mix-blend-mode: screenmultiply7. The bar
7. 质量基准
- — a rocket flying up-right, one barrel roll per loop; a flame from
examples/rocket.specimen.jsthat lights the tile; chrome band and nozzle;gradientporthole.mat.light - — a thick bevelled bolt; the one dynamic texture runs a charge down it and pools it in the tip.
examples/bolt.specimen.js - — a lathed bell that rings twice per loop, a chrome clapper lagging behind; a
examples/bell.specimen.jsbadge that pops when it rings.mat.glow - — a padlock whose chrome shackle lifts and swings open once per loop while the keyhole lights;
examples/lock.specimen.jskeeps the lifted shackle off the top.fit.y - — a chip with chrome pins on every side; a
examples/chip.specimen.jsdie where square pulses of light, drawn through the dynamic texture, ripple outward twice per loop.mat.glass - — a colourless puzzle cube: 26 cubies, every sticker a different finish of the same black (white in light), layers that snap a quarter turn four times per loop and close solved. The neutral palette: it reads by finishes alone.
examples/cube.specimen.js - — a keyboard keycap on a perforated chrome plate, a speckled top over a matte skirt, a lit return-arrow legend; it presses twice per loop with a bounce while the light under it flares.
examples/key.specimen.js - — two meshed chrome gears, the large one with lightening holes, that tick one tooth at a time four times per loop while the hub flares. Rotating parts with symmetric geometry keep plain chrome: a textured finish would turn with them and break the loop.
examples/gears.specimen.js
- ——一枚向右上方飞行的火箭,每轮循环完成一次整圈滚转;由
examples/rocket.specimen.js生成并照亮 tile 的火焰;镀铬环带和喷嘴;gradient舷窗。mat.light - ——一枚粗壮、圆润倒角的闪电;唯一的动态纹理让一道充能光沿其向下移动,并汇聚在尖端。
examples/bolt.specimen.js - ——一只通过 lathe 构建的铃铛,每轮循环鸣响两次;镀铬铃舌滞后摆动;
examples/bell.specimen.js徽章在铃响时弹出。mat.glow - ——一把挂锁,其镀铬锁梁在每轮循环中抬起并摆动打开一次,同时锁孔亮起;
examples/lock.specimen.js可防止抬起的锁梁超出顶部。fit.y - ——一块四边带有镀铬引脚的芯片;
examples/chip.specimen.js骰子上的方形光脉冲通过动态纹理绘制,并在每轮循环中向外扩散两次。mat.glass - ——一个无色魔方:26 个小方块,每张贴纸采用相同的黑色但不同 finish(浅色主题中则为白色);各层每轮循环咔哒转动四分之一圈,共四次,最终回到已解状态。使用中性 palette:仅依靠 finish 就能清晰呈现。
examples/cube.specimen.js - ——一个位于 perforated 镀铬底板上的键盘键帽,speckled 顶部搭配 matte 裙边,带有发光的回车箭头图例;它每轮循环按压两次并产生回弹,同时下方光线增强。
examples/key.specimen.js - ——两个相互啮合的镀铬齿轮,大齿轮带有减重孔;它们每轮循环逐齿转动四次,同时轮毂产生辉光。采用对称几何体的旋转部件应保持普通镀铬材质:带纹理的 finish 会随部件一起转动,从而破坏循环。
examples/gears.specimen.js
8. Never
8. 绝不执行
- Never edit , and never ship a changed copy of
runtime/.specimen.js - Never construct a material or pick a colour in the piece; every surface comes from .
mat.* - Never call twice, or import anything in the piece.
texture() - Never run , add a dependency, or create a folder of your own in the person's project.
npm install - Never call live "path traced": it is raster.
- Never render before prints
validate.mjs.ok
- 绝不编辑 ,也绝不交付修改过的
runtime/副本。specimen.js - 绝不在 piece 中创建材质或选择颜色;所有表面都必须来自 。
mat.* - 绝不调用两次 ,也绝不在 piece 中导入任何内容。
texture() - 绝不运行 、添加依赖,或在对方项目中创建自己的文件夹。
npm install - 绝不能将 live 称为 path traced;它是 raster。
- 在 输出
validate.mjs之前,绝不进行渲染。ok