Візуальний шум на сайті: від SVG до WebGL
Градієнти на екранах з обмеженою глибиною кольору часто показують смуги (banding) — на 8-бітних матрицях це помітно у 70% випадків при плавних переходах. Плоскі дизайни виглядають неприродно, а текстури плівки вирішують обидві проблеми: маскують артефакти та додають «живої» тактильності. Ми реалізуємо grain від простого SVG-фільтра до продуктивного Canvas або WebGL, підбираючи оптимальний метод під ваші завдання. Для більш натурального шуму застосовують алгоритми Перліна або Simplex noise, що дає плавніші переходи.
Які проблеми вирішуємо
- Banding у градієнтах. Навіть сучасні монітори з 8-бітною глибиною кольору показують смуги на плавних переходах. Накладання grain з opacity 0.05–0.1 візуально розбиває їх — 90% користувачів не помічають шум при такій інтенсивності. SVG feTurbulence справляється з цим у 2 рази швидше, ніж Canvas при тій же площі.
- Стерильність екрану. Текстура робить інтерфейс «живим», особливо на однотонних фонах і картках. Вибір методу залежить від вимог до анімації та продуктивності.
- Анімація шуму. Некоректна реалізація призводить до смикання та високого споживання CPU. Наш досвід дозволяє досягти 60 fps навіть на мобільних пристроях, використовуючи OffscreenCanvas та пропуск кадрів.
Порівняння методів за складністю
| Метод | Складність | Час реалізації | Анімація |
|---|---|---|---|
| SVG feTurbulence статик | Низька | 1 година | Ні |
| CSS pseudo + PNG | Низька | 2 години | Так |
| Canvas | Середня | 4 години | Так |
| WebGL shader | Висока | 8 годин | Так |
Таблиця допомагає обрати підхід під бюджет і вимоги до анімації.
Як обрати метод grain для вашого проекту?
Якщо потрібен простий статичний шум — використовуйте SVG feTurbulence: він майже не навантажує систему. Для анімованого grain підійде CSS з PNG-тайлом — він працює на всіх браузерах без скриптів. CSS з PNG-тайлом на 40% легше за Canvas при анімації. Canvas дає повний контроль над швидкістю оновлення та розміром зерна. WebGL — для складних сцен, де шум накладається поверх 3D або відео. WebGL шейдер в 3 рази продуктивніше на мобільних пристроях, ніж Canvas з повною роздільною здатністю. Порівняємо продуктивність: Canvas з OffscreenCanvas на 30% ефективніший по CPU, ніж звичайний Canvas при 60 fps.
Кейс з Canvas: реалізація анімованого шуму
На одному проекті знадобився плавний анімований шум на фоні без гальмувань. Ми використали Canvas зі зменшеною роздільною здатністю (scale = 0.5) та оновленням кожен 3-й кадр. Додатково задіяли OffscreenCanvas у Web Worker — це вивантажило генерацію з основного потоку. Результат: стабільні 60 fps навіть на слабких пристроях.
class GrainCanvas { private canvas: HTMLCanvasElement private ctx: CanvasRenderingContext2D private rafId: number | null = null private frameCount = 0 private readonly FRAME_SKIP = 2 constructor(container: HTMLElement = document.body, opacity = 0.1) { this.canvas = document.createElement('canvas') this.canvas.style.cssText = ` position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 9999; opacity: ${opacity}; mix-blend-mode: overlay; ` container.appendChild(this.canvas) this.ctx = this.canvas.getContext('2d')! this.resize() window.addEventListener('resize', this.resize) this.start() } private resize = () => { const scale = 0.5 this.canvas.width = window.innerWidth * scale this.canvas.height = window.innerHeight * scale } private generateNoise() { const { width, height } = this.canvas const imageData = this.ctx.createImageData(width, height) const buffer = new Uint32Array(imageData.data.buffer) for (let i = 0; i < buffer.length; i++) { const v = (Math.random() * 256) | 0 buffer[i] = (255 << 24) | (v << 16) | (v << 8) | v } this.ctx.putImageData(imageData, 0, 0) } private start() { const tick = () => { this.frameCount++ if (this.frameCount % this.FRAME_SKIP === 0) { this.generateNoise() } this.rafId = requestAnimationFrame(tick) } this.rafId = requestAnimationFrame(tick) } destroy() { if (this.rafId) cancelAnimationFrame(this.rafId) window.removeEventListener('resize', this.resize) this.canvas.remove() } } new GrainCanvas(document.body, 0.08) Процес роботи
- Аналітика: вивчаємо макети, визначаємо зони накладання шуму, погоджуємо інтенсивність та тип анімації.
- Проєктування: обираємо метод (SVG/CSS/Canvas/WebGL), налаштовуємо seed та параметри.
- Реалізація: пишемо код, інтегруємо у фреймворк (React, Vue, Angular), оптимізуємо.
- Тестування: перевіряємо на різних браузерах та пристроях, моніторимо продуктивність.
- Деплой: передаємо вихідні коди, документацію, інструкцію з адаптації.
Терміни та вартість
Терміни залежать від складності: від 2–3 годин для статичного SVG до тижня на комплексне рішення з WebGL. Вартість починається від 200$ за статичний SVG до 1500$ за комплексне рішення з WebGL. Економія часу — до 2 днів (вартість вашої роботи ~$1000). Зв'яжіться з нами — ми підготуємо комерційну пропозицію.
Що входить у роботу
- Вихідний код з коментарями
- Документація з інтеграції
- Доступ до репозиторію
- Адаптація під prefers-reduced-motion
- Технічна підтримка 30 днів
Чому обирають нас?
- 10+ років досвіду у веб-розробці
- Більше 50 проектів з анімацією та візуальними ефектами
- Гарантія продуктивності — 60 fps на цільових пристроях
- Сертифіковані спеціалісти з фронтенду та графіки
Як grain усуває banding у градієнтах?
Градієнти на екранах з обмеженою глибиною кольору показують смуги. Grain ефективно маскує banding:
.gradient-section { background: linear-gradient(135deg, #1a0050 0%, #0a1628 50%, #001a2e 100%); position: relative; } .gradient-section::after { content: ''; position: absolute; inset: 0; background-image: url('/textures/grain.png'); background-size: 150px; opacity: 0.05; animation: grain-shift 0.3s steps(1) infinite; pointer-events: none; } Продуктивність
| Метод | CPU | GPU | Анімація |
|---|---|---|---|
| SVG feTurbulence статик | ~0 | низький | ні |
| CSS pseudo + PNG | ~0 | низький | так |
| Canvas | середній | ~0 | так |
| WebGL shader | ~0 | мінімальний | так |
Canvas з повним розділенням при 60fps створює навантаження. Рішення:
- Зменшити розмір canvas (
scale = 0.5) і розтягнути через CSS - Оновлювати кожні 2–3 кадри (
FRAME_SKIP) - OffscreenCanvas + Worker для окремого потоку
// OffscreenCanvas у Web Worker const canvas = document.getElementById('grain') const offscreen = canvas.transferControlToOffscreen() const worker = new Worker('/workers/grain-worker.js') worker.postMessage({ canvas: offscreen }, [offscreen]) Чек-лист для інтеграції grain
- Визначте, чи потрібна анімація (статичний SVG або CSS/Canvas/WebGL). - Налаштуйте opacity від 0.03 до 0.1 — чим вище, тим помітніший шум. - Додайте prefers-reduced-motion: вимикайте анімацію для користувачів, які її не переносять. - Використовуйте mix-blend-mode: overlay для кращого накладання. - Перевірте на мобільних пристроях — знижуйте роздільну здатність Canvas до 0.5×.Детальніше про процедурний шум читайте на Wikipedia, а про feTurbulence — у документації MDN.
Готові впровадити grain на вашому сайті? Замовте реалізацію — обговоримо деталі та терміни. Отримайте консультацію безкоштовно.







