mcpField
S
specimen

specimen

github.com/lucasmarkes设计与 UI1617 次安装1 周前更新
代码仓库

关于 specimen

specimen 是面向编码 Agent 的技能,通过编写单个 piece 文件制作主题化循环 3D 图标。它使用固定的渲染运行时,并支持生成视频或实时栅格内容。

主要功能

  1. 01单文件图标作品
  2. 02深色与浅色主题
  3. 03循环 3D 动画
  4. 04视频或实时栅格输出
  5. 05校验与联系表脚本

适用场景

  1. 01为项目制作产品图标
  2. 02生成深色与浅色主题图标
  3. 03将图标导出为视频或实时栅格内容

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/lucasmarkes/skills --skill specimen

安装后在 Claude Code 中输入/specimen确认已加载。

详细介绍

Specimen:制作图标

你正在制作一个 Specimen 图标:一个置于柔和圆角底板上的单体光泽物件,如同陈列柜中的展品般受光。色彩只以光的形式呈现。动画循环衔接自然。从一个作品文件即可生成深色和浅色主题,既可以是路径追踪视频,也可以是实时栅格元素。

examples/ 中的作品是标准。动笔前,先阅读最接近需求的作品。如果你制作的图标放在它们旁边显得格格不入,那就还没完成。

你只编写一项内容:作品文件 <name>.specimen.js。底板、摄影棚、灯光、后期处理、主题、路径追踪器、降噪器和编码器均已固定,存放在 runtime/ 中。绝不要编辑 runtime/,绝不要粘贴一份修改过的副本,也绝不要重新实现它已有的功能。如果你认为运行时缺少某项功能,请告知对方,不要另辟蹊径绕过它。

下文提到的所有文件都位于此技能的文件夹中。作品写入对方的工作目录(或对方指定的位置);媒体文件放在对方指定的位置(public/icons/,……),或放在作品文件旁边,并以图标名称作为前缀。绝不要在对方的项目中自行创建文件夹。请从对方的工作目录中,通过文件在此文件夹中的路径运行脚本。

Specimen 仅指此技能。它没有 npm 包,也不需要安装步骤。绝不要运行 npm install,绝不要向对方的项目添加依赖,也绝不要在作品中导入 three:所有内容都通过 ctx 提供。

技能文件夹
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)

脚本使用纯 node 运行(版本 22 或更高),且只使用 Node 内置模块。它们会驱动机器上已有的 Chrome(或 SPECIMEN_CHROME);生成视频需要 ffmpeg。如果缺少其中任一项,脚本会指出具体缺少什么:请明确告知对方缺少的项目以及获取方式(brew install ffmpeg);不要自行安装任何内容。


0. 交付内容

在讨论任何概念前,先用一条消息询问对方:

Video(默认)——路径追踪,生成 <name>-dark.webm/.mp4 和 <name>-light.webm/.mp4,每个主题各带一张海报图,并提供 <video> 代码片段。效果最佳,每种主题占用数百 KB,可在各处播放。 Live ——生成 specimen.js(含许可证文件),并将作品复制到对方的静态文件夹中;每种主题各带一张海报图,并提供 <specimen-icon> 代码片段。它是栅格图,而非路径追踪:反射效果较柔和,且不使用降噪器。请直说这一点。它需要 WebGPU;不支持 WebGPU 或启用了减少动态效果时,会回退到海报图。 Both ——默认生成视频;只有在对方启用的位置才额外使用实时版本。

除非对方只要求一种主题,否则应渲染两种主题。同时询问文件的保存位置;如果对方没有说明,就将文件放在作品文件旁边。如果没有人可以询问,就选择视频、两种主题和作品文件旁边,并说明这一点。

尺寸

请按图标实际显示的尺寸渲染:视频尺寸一旦被拉伸到超过其像素尺寸,边缘就会变模糊,没有任何编码设置能恢复清晰度。先在项目中找到图标所在 CSS 或组件里的宽度,并查看其最宽断点。只有无法判断时,才在询问交付方式的同一条消息中询问:

显示尺寸(CSS px)--size视频每种主题
不超过 160384320 px约 4 分钟
不超过 320768640 px约 17 分钟
不超过 4801152960 px约 36 分钟

视频尺寸必须至少是 CSS 宽度的两倍,才能适配 Retina 屏幕;最终视频尺寸为 --size 的 5/6:选择能覆盖该宽度的第一行。宽度超过 480 时,--size 为宽度的 2.4 倍,并向上取整到 12 的倍数,耗时则随尺寸的平方增长。对于实时版本,尺寸只决定海报图的尺寸。如果没有人可以询问,也没有内容可供查阅,就选择 768 并说明这一点。

1. 概念

阅读 concepts.md。提出两到三个概念,每个概念占一行:

名称。 物体 · 动起来的部分 · 强调色的位置 · 表面效果。

示例:

闪电。 一个厚实、边缘柔和的闪电 · 一道光每轮循环两次自上而下流过它,并汇聚在尖端 · tile 下方的电荷与光池 · 光泽主体。

然后为强调色提出两到种调色板:这是图标唯一使用的颜色,让对方选择或指定自己的颜色:

调色板。 青色 #22d3ee(冷静、科技感) · 琥珀色 #f59e0b(温暖、警示) · 中性色 #a1a1aa(完全不带颜色:物体仅凭表面效果来呈现)。

颜色由对方决定:他们的品牌、版块和网格。根据产品领域和页面上相邻的图标提出建议(安全主题适合冷色,警示适合暖色,单色网格适合中性色);绝不能默默替对方选择。调色板会设置 scene.accent;如果他们希望发光颜色不同,还会设置 scene.glow。

等待对方选择。只有对方已经选好了物体和动画时才跳过这一步。如果没有人可以询问,就选择轮廓最鲜明的概念,并说明你选了哪个。

优秀概念的特点:

  • 一个物体,一个想法。 需要配上说明文字才能理解的概念,还不算合格。
  • 先看轮廓。 眯起眼睛看:如果仅凭轮廓无法辨认物体,就换一个。
  • 物体在产品中有意义。 身份验证用锁,警示用铃。参见 concepts.md。
  • **用一个动作讲述故事,**幅度小、速度慢:旋转、摆动、充能、脉动。不要同时做三件事。

2. 阅读最接近的示例

打开 examples/ 中与该概念最接近的示例,并通读全文。沿用其结构:scene 块、如何使用 geo.* 构建几何体,以及 update(phase) 如何确保每个项都是周期性的。

3. 编写作品

编写一个包含两个导出且不含导入的 ES 模块:

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
键含义
THREE套件中的 three 实例;作品绝不导入 three
mat下方的材质角色,根据当前主题解析
geo.roundedRect(shape, w, h, r)在 Shape 或 Path(孔洞)上创建圆角矩形;w、h 为半尺寸
geo.bevelExtrude(shape, { depth, bevel, segments, curveSegments })挤出形状并形成柔和的圆角边缘,居中于 z = 0
geo.lathe(profile, segments)将由 [x, y] 对组成的轮廓绕 +Y 旋转
geo.planarUV(geometry, w, h)在 [-w, w] × [-h, h] 范围内生成 0..1 的 UV 坐标,用于动态纹理
texture(w, h)唯一的动态纹理:{ tex, draw(fn(u, v) → [r, g, b]) }。将 tex 作为角色的 emissiveMap,并在 update 中调用 draw。再次调用会抛出错误:路径追踪器为每个对象持有一张动态纹理。详见下文
gradient(stops, { srgb })静态垂直渐变,渐变点为 [t, css colour],从顶部(0)到底部(1);用于火焰和轨迹
accent, glow两种颜色,类型为 THREE.Color,供纹理的 draw 使用
theme'dark' 或 'light',只读;仅在万不得已时使用
TAU, bump(x, c, w), smoothstep(x, a, b)动画辅助函数:bump 是高斯函数 exp(-((x − c) / w)²),在 c 处为 1,超过 2w 后接近 0(但不会恰好为 0);smoothstep 在 a 到 b 之间从 0 平滑过渡到 1
空间和尺寸

图块为 2 × 2 个单位,以原点为中心,朝向摄像机(+x 向右,+y 向上,+z 朝向摄像机)。安全框为 1.16 个单位见方(图块的 58%),中心位于 y = +0.04。将物体做成约 1.0–1.3 个单位宽、最高约 1.6 个单位,并以自身原点为中心,位于 z ≈ 0.45:示例在 fit.scale 为 0.8–0.9 时就是这个尺寸。

scene.fit 会将物体包在一个组中:scale 会以世界原点为中心缩放物体(不会重新居中),x、y 则以世界单位移动物体(y: -0.08 表示在图块上向下移动 4%)。系统不会替你居中:如果物体在画布上偏离中心,先将其几何体居中(geometry.center(),或在组内移动各个部件),再调整 fit。

在 64 px 尺寸下仍清晰可见的厚度:管和杆的半径至少为 0.12,倒角至少为 0.05。主光源是位于上方偏右的柔光箱,因此负值的 rotation.y(约为 −0.3,会让物体右侧转向摄像机)能让倒角更好地接住光线。

动态纹理

draw(fn) 会对每个纹素调用一次 fn(u, v),其中纹理范围内的 u 和 v 为 0..1(v = 0 是第一行),纹理会通过网格自身的 UV 读取。拉伸体和扁平形状需要使用 geo.planarUV(geometry, w, h),它会将 v = 0 映射到底部(y = −h),将 u = 0 映射到左侧。车削体、管和球体已有 UV(TubeGeometry 的 u 沿路径延伸)。返回 0..1 范围内的线性颜色:超出范围的值会被截断,emissiveIntensity(螺栓使用 3)会对其进行缩放。使用 glow.r * k, glow.g * k, glow.b * k,其中 k 为 0..1;在物体应保持黑色的区域,保留深色底色(k ≈ 0)。沿路径的色带使用 texture(256, 16) 即可,表面则使用 128 × 128:draw 每帧都会在 CPU 上运行。

材质用途

所有材质都来自 mat.*。切勿自行创建材质(new THREE.MeshStandardMaterial(…) 会导致验证失败):手动创建的材质只有一种颜色,而浅色主题需要另一种颜色。

用途深色主题浅色主题用于
mat.body(o)亮面黑色白色釉面陶瓷物体主体
mat.chrome(o)铬色铬色圆环、带、铰链、卸扣
mat.glass(o)深色玻璃白色内芯上的透明玻璃骰子、镜片、屏幕
mat.inlay(o)比黑色略浅珍珠灰主体上的凸起细节
mat.glow(k, o)发光颜色的自发光发光颜色作为表面颜色,并带微弱自发光徽章、钥匙孔、尖端珠:仅用于一个小型发光部件,绝不可用于主体
mat.light(k, o)暖白色发光温暖、微弱的光芒窗户、舷窗、火焰

o 仅用于覆盖物理属性:finish、finishRepeat、roughness、metalness、clearcoat、normalMap、side、transparent、depthWrite、emissiveMap、alphaMap、emissiveIntensity。传入颜色(color、emissive 等)会抛出错误:颜色由主题决定。动态纹理在深色主题下通过 emissiveMap 发光,在浅色主题下则成为白色陶瓷上的颜料,因此同一个 draw 两种主题都适用。mat.glow 部件是浅色主题中显示强调色的唯一部位,因此它必须保持小巧:徽章、珠子、钥匙孔。

表面处理

每个角色都采用一种表面处理:mat.body({ finish: 'brushed', finishRepeat: 5 })。表面处理会设置物理属性(对于带纹理的处理,还会设置精细的静态贴图),同时保留角色的颜色,因此在两种主题下都适用。部件自身传入的值优先于表面处理。

表面处理光线照射时的效果适用对象
gloss清漆下的镜面效果:清晰映出摄影棚灯条应呈现漆面效果的主体
matte无反射,仅有柔和的形体明暗裙摆、核心、需要弱化存在感的部件
brushed带细纹的金属:高光沿纹理延展成一道光带板件、框架、套环、锁体
perforated带圆孔网格的金属,每个孔都能捕捉到自己的高光格栅、键帽板、扬声器面板
speckle粗糙表面上稀疏分布着明亮的闪光点,如石材或闪粉漆盖帽、键帽、铃铛主体

finishRepeat 表示每个 UV 单位上的重复次数(默认值为 10)。挤出体使用场景单位的 UV,因此无论尺寸如何,纹理间距都相同;车削体和管体的 UV 范围为 0..1,因此适合使用较小的数值(3–5)。精细的表面处理看起来像材质,粗糙的则像印刷图案(过宽的拉丝纹路看起来像木纹)。

绘制清晰线条的两条细灯带可通过 scene.lights.strips 缩放(0–1,默认值为 1)。当物体旋转时,如果精细倒角扫过灯带,请调低此值:反射比一个像素还细,会在不同帧间闪烁。螺栓使用 strips: 0。

validate.mjs 会将源代码作为文本读取,因此即使注释中包含 …Material(,也会导致 materials 检查失败。

制作规则
  • 一个物体,一个想法。 不要添加第二个物体、道具或文字。
  • 先看轮廓。 在 64 px 下能辨认出来,否则就不合格。
  • 每个图标都要有精致的光影。 摄影棚使用宽大的柔光箱和两条细长的硬光灯带:灯带会沿倒角和边缘勾勒出清晰的光线。图标至少要采用两种表面处理,让这些光线在图标不同区域呈现不同特征(例如,哑光裙摆上的亮面顶部,或斑点主体周围的拉丝框架)。图标应通过表面受光的方式来呈现,而不只是依靠轮廓。
  • 点缀色只能以光的形式出现。 主体在深色主题下保持黑色,在浅色主题下保持白色。点缀色只能以发光效果的形式出现在瓷砖下方的光池和边缘上。绝不要将表面涂成点缀色。
  • 适配 58% 的框内区域。 实体主体位于安全区域内,略微高于中心,并且与瓷砖边缘始终至少相距 4%。使用 scene.fit 定位,不要调整物体自身的位置。
  • 循环不能跳变。 update 中的每一项都必须是 phase · TAU 的整数谐波函数,或是在循环两端均为 0 的 smoothstep 包络。Phase 1 的效果必须与 Phase 0 完全一致。
  • 光效必须留在瓷砖内。 溢出瓷砖边缘的发光和光晕无法通过验证。
  • 绝不要编辑 runtime/。

4. 生成图集 → 检查 → 修复

node <skill>/sheet.mjs <piece...> [--frames 0,60,120,180] [--guide] [--pt] [--out sheet.png]

它会将第 0、60、120 和 180 帧(或 --frames 指定的帧)渲染为 256 px 的单元格:每个部件各有一行深色主题和一行浅色主题;使用 --guide 时会以紫色绘制 58% 的安全框。--pt 会在每行末尾添加第五个单元格:对第 0 帧进行路径追踪并降噪。每个部件的光栅渲染需要几秒钟;使用 --pt 每个部件还会增加约半分钟。将一个示例放在你的部件后面,即可按行对比。

查看 PNG。检查以下内容:轮廓是否清晰;每一帧中的主体是否都位于框内;浅色主题行的表现是否不逊于深色主题行(白色背景上的发光效果清晰可见,没有灰暗浑浊的效果);各帧之间的动作是否明显。修复并重复检查,直到它能与示例并列展示。完整渲染前至少运行一次 --pt:光栅单元格比最终效果更亮,而在光栅渲染中清晰可见的黑色部件,在路径追踪后的深色瓷砖上可能会变得不明显。要在 256 px 下检查细节,可在临时目录中复制一份,并增大 fit.scale;绝不要在他人的项目中这样做。

5. 验证

node <skill>/validate.mjs <name>.specimen.js

它会打印每条未通过规则及其原因和编号;任何规则未通过时,都会以状态码 1 退出。重复操作,直到它打印 ok。

规则未通过条件
contract未导出 scene 或 object;accent 不是十六进制颜色;duration 不在 4–12 范围内;object() 抛出异常或未返回 mesh
materials源代码中出现 …Material( 构造函数
seam相位 0 与相位 1 的栅格图像平均差异超过 0.5(0–255)
fit实体部分(对象轮廓中间的 90%,透明网格(如火焰和光环)除外;不透明的 mat.glow 部分计入)超出安全框超过图块的 3%,或者整个轮廓距图块边缘不足 4%;检查循环中第 0、¼、½ 和 ¾ 帧
light-in-tile图块外任何区域的亮度比页面高出 2/255 以上
one-texture调用了两次 texture()

6. 渲染并交付

开始前,根据 §0 中的尺寸表告知预计耗时:在近期的 Mac 上,--size 768 每帧约需 4 秒,因此 8 秒的循环(240 帧)每种主题约需 17 分钟,两种主题约需 35 分钟。仅实时模式大约一分钟,只渲染两张海报。视频渲染耗时会超过任何前台命令的超时限制,因此请在后台运行并查看进度行(每 30 帧显示一次,如 30/240 · 666 s left),直到打印出文件;不要在前台运行,否则超时会导致渲染中途终止。

node <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 主题,还是提供了切换按钮([data-theme])。打印的视频代码片段会跟随 OS 主题:

<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>

对于带有 [data-theme] 切换按钮的网站,提供两个视频和用于选择其中一个的 CSS:

<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>

实时模式:

<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>

除非设置了 theme="dark|light",否则 <specimen-icon> 会跟随 prefers-color-scheme(在带有 [data-theme] 的网站上,请通过切换按钮设置该属性);它会在离开屏幕时暂停,并在缺少 WebGPU 或设置了减少动态效果时保留海报:浅色主题使用 poster-light,其他情况使用 poster。通过 CSS 的 width 调整其大小(它会保持正方形;默认是 160 px)。

每一帧中,图块外的区域都与页面颜色完全一致(黑色或白色)。若图标位于其他颜色的版块上,请使用混合模式融入背景:深色主题使用 mix-blend-mode: screen,浅色主题使用 multiply。

7. 标准

  • examples/rocket.specimen.js — 一枚向右上方飞行的火箭,每次循环翻滚一周;火焰来自 gradient,会照亮瓷砖;配有镀铬环带和喷嘴;舷窗采用 mat.light。
  • examples/bolt.specimen.js — 一枚厚实的倒角螺栓;唯一的动态纹理会让能量沿螺栓流动,并汇聚到尖端。
  • examples/bell.specimen.js — 一口车削成形的铃铛,每次循环鸣响两次,镀铬铃锤滞后摆动;铃铛鸣响时,mat.glow 徽章会弹出。
  • examples/lock.specimen.js — 一把挂锁,每次循环中镀铬锁梁抬起并摆动打开一次,同时钥匙孔亮起;fit.y 可确保抬起的锁梁不会超出顶部。
  • examples/chip.specimen.js — 一枚四周都有镀铬引脚的芯片;mat.glass 晶粒中,方形光脉冲通过动态纹理绘制,向外荡漾,每次循环两次。
  • examples/cube.specimen.js — 一个无彩色的魔方:26 个小方块,每个贴纸都是同一种黑色的不同表面质感(在浅色模式下为白色);各层每次循环转动四次四分之一圈,并合拢还原。中性色调:仅凭表面质感就能看出区别。
  • examples/key.specimen.js — 一个键盘键帽,置于穿孔镀铬板上;键帽顶面带有斑点纹理,裙边为哑光,回车箭头图例会发光;每次循环按下两次,带有回弹效果,同时下方的灯光会增强。
  • examples/gears.specimen.js — 两个啮合的镀铬齿轮,大齿轮带有减重孔;每次循环逐齿啮合四次,同时轮毂会增强光亮。几何形状对称的旋转部件应保持普通镀铬表面:纹理表面会随其旋转,从而破坏循环。

8. 禁止事项

  • 绝不要编辑 runtime/,也不要提交修改过的 specimen.js 副本。
  • 绝不要在作品中构建材质或挑选颜色;每个表面都应来自 mat.*。
  • 绝不要调用两次 texture(),也不要在作品中导入任何内容。
  • 绝不要运行 npm install、添加依赖,或在对方的项目中自行创建文件夹。
  • 绝不要把实时渲染称为“路径追踪”;它是光栅渲染。
  • 在 validate.mjs 输出 ok 之前,绝不要渲染。

评论

支持 Markdown