Візуальний шум на сайті: від 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 на вашому сайті? Замовте реалізацію — обговоримо деталі та терміни. Отримайте консультацію безкоштовно.







