- 74 brands: airbnb, stripe, apple, figma, linear, vercel, etc. - 147 files: DESIGN.md + README.md per brand (~2.1 MB) - Each DESIGN.md contains structured brand analysis (colors, typography, components) - Updated 00-overview.md with new design-systems directory
7.8 KiB
7.8 KiB
Deterministic Generation — Seed-Based Layout Methodology
Детерминированная генерация паттернов с использованием seeded PRNG. Основано на patterngen-oss.
Обзор
Каждая генерация паттернов детерминирована — одинаковый seed всегда даёт идентичный layout. Это позволяет:
- Повторно воспроизводить удачные комбинации
- Синхронизировать дизайн между Figma и PatternGen
- Кэшировать результаты генерации
- Делиться сценами через JSON с seed
Seeded PRNG
Алгоритм
class SeededRNG {
private seed: number;
constructor(seed: number) {
this.seed = seed & 0x7FFFFFFF; // 31-bit positive
}
next(): number {
// LCG: Linear Congruential Generator
this.seed = (this.seed * 1103515245 + 12345) & 0x7FFFFFFF;
return this.seed / 0x7FFFFFFF; // range [0, 1)
}
nextInt(min: number, max: number): number {
return Math.floor(this.next() * (max - min + 1)) + min;
}
pick<T>(array: T[]): T {
return array[this.nextInt(0, array.length - 1)];
}
}
Seed из Figma
{
"seed": 123456789
}
31-bit integer генерируется плагином Figma. Значение зашито в JSON и передается в PatternGen.
Placement Engine
Input parameters
| Параметр | Тип | Диапазон | Описание |
|---|---|---|---|
seed |
int | 0–2³¹-1 | Seed для PRNG |
density |
int | 1–20 | Количество элементов |
proximity |
int | 1–20 | Мин. расстояние до title (20 px grid units) |
Placement algorithm
1. Создать RNG из seed
2. Для каждого элемента:
a. Сгенерировать случайную позицию (x, y)
b. Проверить proximity к существующим элементам и title
c. Если too close → пересадить с тем же RNG state
d. Иначе → записать позицию
3. Для каждого элемента:
a. Выбрать случайный pattern из доступных
b. Выбрать случайную clipSide (top/bottom/left/right)
c. Выбрать случайную анимацию
d. Сгенерировать stagger delay
Stagger calculation
const staggerWindow = Math.max(0, Math.min(0.85, (stagger / 5) * 0.8));
const staggeredStart = element.animDelay * staggerWindow * durationMs;
const elemDur = durationMs * (1 - staggerWindow);
const localMs = cycleMs - staggeredStart;
const p = Math.max(0, Math.min(1, localMs / elemDur));
Animation Timeline
Фаза 1: Title Reveal (0 — durationMs)
Title проявляется из центра по горизонтали (если wider) или вертикали (if taller).
const titleT = min(1, max(0, elapsedMs / durationMs));
const titleEased = applyEasing(titleT, easing);
if (title.w >= title.h) {
// wide title: half-reveal horizontally
halfReveal = (title.w / 2) * titleEased;
revealBox = {
x: cx - halfReveal,
y: title.y,
w: halfReveal * 2,
h: title.h
};
} else {
// tall title: half-reveal vertically
halfReveal = (title.h / 2) * titleEased;
revealBox = {
x: title.x,
y: cy - halfReveal,
w: title.w,
h: halfReveal * 2
};
}
Фаза 2: Pattern Reveal (durationMs — 2× durationMs)
Паттерны проявляются с stagger-задержкой.
Фаза 3: Dots Loop (2× durationMs — 4× durationMs)
Точки пульсируют синусоидой 4000ms цикл.
const fadeIn = min(1, loopMs / 800);
const wave = 0.5 + 0.5 * sin(2 * PI * dot.blinkSpeed * (loopMs / 1000) + dot.blinkPhase * 2 * PI);
const pulse = 0.15 + 0.85 * wave;
opacity = pulse * fadeIn;
Color System
Fixed Colors
const FIXED_COLORS = ['#000000', '#FFFFFF'];
Черный и белый всегда доступны.
Custom Colors
До 3 кастомных цветов добавляются через color picker. Color pairs для паттернов генерируются алгоритмически:
// Для каждого pair custom_color:
bg = customColor;
fg = generateComplementary(customColor);
SVG Colorization
function colorizeSvg(svgText: string, colors: ColorPair): string {
return svgText
.replace(/fill="white"/g, `fill="${colors.bg}"`)
.replace(/fill="black"/g, `fill="${colors.fg}"`)
.replace(/stroke="black"/g, `stroke="${colors.fg}"`);
}
SVG-паттерны хранят только white (фон) и black (foreground).
Окрашивание происходит в runtime.
Image Caching
const imageCache = new Map<string, HTMLImageElement>();
export function svgToImage(svgText: string): Promise<HTMLImageElement> {
if (imageCache.has(svgText)) return Promise.resolve(imageCache.get(svgText)!);
return new Promise((resolve, reject) => {
const img = new Image();
const blob = new Blob([svgText], { type: 'image/svg+xml;charset=utf-8' });
const url = URL.createObjectURL(blob);
img.onload = () => {
URL.revokeObjectURL(url);
imageCache.set(svgText, img);
resolve(img);
};
img.onerror = () => {
URL.revokeObjectURL(url);
reject(new Error('Failed to load SVG image'));
};
img.src = url;
});
}
export function clearImageCache() {
imageCache.clear();
}
Export Pipeline
Layer structure
titles: durationMs (1×) — plays once
patterns: durationMs (1×) — plays once
dots: 4000ms loop — infinite loop
Export scale
- Canvas: 1920×1080
- Export Scale: 4× → 7680×4320
- FPS: 24
- Format: PNG (RGBA, transparent background)
Frame calculation
const totalFrames = Math.ceil((layerDuration / 1000) * FPS);
const frameRatio = totalFrames > 1 ? frame / (totalFrames - 1) : 1;
Naming convention
<sceneName>_<layer>_<frame>.png
Пример:
MyTitle_titles_00000.png
MyTitle_titles_00001.png
MyTitle_patterns_00000.png
MyTitle_dots_00000.png
Использование determinism в дизайне
Scenario 1: Figma → PatternGen sync
- Экспортировать сцену из Figma (содержит seed)
- Загрузить в PatternGen
- Паттерны генерируются точно так же каждый раз
- Можно итерировать параметры и снова генерировать тот же layout
Scenario 2: A/B testing
- Создать базовую сцену с seed
- Экспортировать 10 раз с разными параметрами density/stagger
- Каждый раз layout идентичен, но timing разный
- Сравнить конверсию/визуальное впечатление
Scenario 3: Team collaboration
- Дизайнер делится scene JSON
- Коллега загружает и видит ту же генерацию
- Может менять цвета, паттерны, параметры
- Seed остаётся — layout predictable
Референсы кода
src/core/rng.ts— SeededRNG класс, LCG algorithmsrc/core/placement.ts— Placement engine, proximity checksrc/engine/timeline.ts— Phase 1/2/3, dots loopsrc/export/exporter.ts— PNG export, frame calculationsrc/export/renderer.ts— Canvas rendering per framesrc/types.ts— AnimationState, PatternAnimType