algorithmic-art-jp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited algorithmic-art-jp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
日本の伝統的な紋様・技法をアルゴリズムで再現するジェネラティブアートスキルです。和柄パターン生成、書道シミュレーション、枯山水ジェネレーター、日本美術に由来する数理パターンをコードで表現します。出力は p5.js、Canvas API、SVG に対応します。
function drawIchimatsu(ctx, cols, rows, cellSize, color1, color2) {
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
ctx.fillStyle = (x + y) % 2 === 0 ? color1 : color2;
ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
}
}
}function drawAsanoha(ctx, cx, cy, size) {
/* 正六角形の中心から6本の線を放射し、各三角形内に菱形を描画 */
const angles = [0, 60, 120, 180, 240, 300];
const vertices = angles.map(a => ({
x: cx + size * Math.cos(a * Math.PI / 180),
y: cy + size * Math.sin(a * Math.PI / 180)
}));
/* 外枠の六角形 */
drawPolygon(ctx, vertices);
/* 中心から各頂点への放射線 */
vertices.forEach(v => drawLine(ctx, cx, cy, v.x, v.y));
/* 各三角形の中線を追加して麻の葉形状を完成 */
for (let i = 0; i < 6; i++) {
const mid = midpoint(vertices[i], vertices[(i + 1) % 6]);
drawLine(ctx, cx, cy, mid.x, mid.y);
drawLine(ctx, mid.x, mid.y, vertices[i].x, vertices[i].y);
drawLine(ctx, mid.x, mid.y, vertices[(i + 1) % 6].x, vertices[(i + 1) % 6].y);
}
}function drawSeigaiha(ctx, startX, startY, radius, rows, cols) {
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const offsetX = (row % 2 === 0) ? 0 : radius;
const x = startX + col * radius * 2 + offsetX;
const y = startY + row * radius * 0.7;
/* 同心円の波紋を3〜4層描画 */
for (let i = 4; i > 0; i--) {
ctx.beginPath();
ctx.arc(x, y, radius * (i / 4), Math.PI, 0, false);
ctx.strokeStyle = `rgba(0, 80, 150, ${0.2 + i * 0.15})`;
ctx.stroke();
}
}
}
}| 紋様名 | 説明 | 基本図形 |
|---|---|---|
| 七宝(しっぽう) | 円を1/4ずつ重ねた連続模様 | 円の交差 |
| 亀甲(きっこう) | 正六角形の連続配置 | 正六角形 |
| 鱗(うろこ) | 正三角形の交互配置 | 正三角形 |
| 矢絣(やがすり) | 矢羽根を交互に並べた模様 | 平行四辺形 + 三角形 |
| 紗綾形(さやがた) | 卍を斜めに崩した連続模様 | 折れ線の反復 |
各パターンは drawPattern(ctx, type, x, y, size, options) のような統一インターフェースで呼び出す設計を推奨します。
class BrushSimulator {
constructor(options = {}) {
this.pressure = options.pressure || 0.5; /* 筆圧 (0-1) */
this.speed = options.speed || 1.0; /* 速度 */
this.angle = options.angle || Math.PI / 4; /* 筆の傾き */
this.inkAmount = options.inkAmount || 1.0; /* 墨量 (0-1) */
this.bristleCount = options.bristleCount || 8;
}
/* 筆圧による線幅の変化 */
getWidth() {
return this.pressure * 20 + 2;
}
/* 墨量の減少(かすれの再現) */
consumeInk(distance) {
this.inkAmount = Math.max(0, this.inkAmount - distance * 0.001);
return this.inkAmount;
}
}function simulateInkFlow(ctx, x, y, amount) {
/* 紙のにじみ: 放射状に不均一に広がる */
const particles = 50;
for (let i = 0; i < particles; i++) {
const angle = Math.random() * Math.PI * 2;
const dist = Math.random() * amount * 10;
const px = x + Math.cos(angle) * dist;
const py = y + Math.sin(angle) * dist;
const alpha = Math.max(0.02, amount * 0.3 - dist * 0.01);
ctx.fillStyle = `rgba(10, 10, 10, ${alpha})`;
ctx.fillRect(px, py, 1, 1);
}
}function drawSandRipples(ctx, stones, width, height, lineSpacing = 8) {
for (let y = 0; y < height; y += lineSpacing) {
ctx.beginPath();
for (let x = 0; x < width; x++) {
let offsetY = 0;
/* 各石からの影響を計算(距離に反比例する力場) */
stones.forEach(stone => {
const dx = x - stone.x;
const dy = y - stone.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < stone.radius * 4) {
/* 石の周囲で砂紋が同心円状に湾曲 */
offsetY += (stone.radius * 2) / Math.max(dist, 1) *
Math.sin(dist / stone.radius * Math.PI);
}
});
if (x === 0) ctx.moveTo(x, y + offsetY);
else ctx.lineTo(x, y + offsetY);
}
ctx.strokeStyle = 'rgba(160, 150, 130, 0.5)';
ctx.lineWidth = 1;
ctx.stroke();
}
}function placeStones(count, width, height) {
/* 奇数配置の原則(1, 3, 5, 7)*/
/* 三角形構図を基本とし、不等辺三角形で自然さを出す */
const stones = [];
const goldenAngle = Math.PI * (3 - Math.sqrt(5));
for (let i = 0; i < count; i++) {
const r = Math.sqrt(i / count) * Math.min(width, height) * 0.35;
const theta = i * goldenAngle;
stones.push({
x: width / 2 + r * Math.cos(theta),
y: height / 2 + r * Math.sin(theta),
radius: 15 + Math.random() * 25,
type: ['tall', 'flat', 'round'][i % 3]
});
}
return stones;
}function kumikoSubdivision(ctx, polygon, depth, pattern) {
if (depth === 0) { drawFill(ctx, polygon); return; }
const subPolygons = subdivide(polygon, pattern);
/* pattern: 'asanoha', 'goma', 'sakura', 'izutsu' */
subPolygons.forEach(sub =>
kumikoSubdivision(ctx, sub, depth - 1, pattern));
}function drawUkiyoeWave(ctx, baseX, baseY, amplitude, wavelength) {
ctx.beginPath();
for (let x = 0; x < wavelength; x++) {
const t = x / wavelength;
/* 急峻な波頭と緩やかな谷の非対称波形 */
const y = baseY - amplitude * (
Math.pow(Math.sin(t * Math.PI), 0.6) *
(1 + 0.3 * Math.sin(t * Math.PI * 6)) /* 波頭の飛沫 */
);
if (x === 0) ctx.moveTo(baseX + x, y);
else ctx.lineTo(baseX + x, y);
}
ctx.strokeStyle = '#1B3F8B';
ctx.lineWidth = 2;
ctx.stroke();
/* 波頭の白い飛沫をパーティクルで追加 */
}| フォーマット | 用途 | 特徴 |
|---|---|---|
| p5.js | インタラクティブ作品、プロトタイピング | setup() / draw() 構造、簡潔な記述 |
| Canvas API | Webアプリ組み込み、パフォーマンス重視 | ネイティブAPI、細かい制御が可能 |
| SVG | 印刷物、スケーラブル出力 | ベクター形式、CSS アニメーション対応 |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.