Реалізація WebGL-анімацій та 3D-ефектів на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація WebGL-анімацій та 3D-ефектів на сайті
Складна
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація WebGL-анімацій та 3D-ефектів на сайті

WebGL — це не просто «додати красивість». Це програмований графічний конвейєр прямо в браузері з доступом до GPU. Коли потрібно рендерити тисячи частинок, деформувати геометрію за аудіосигналом або будувати інтерактивні 3D-сцени без плагінів — це єдиний інструмент, який впорається без компромісів.

Використовується WebGL 2.0 (поддержка 95%+ браузерів на 2025 рік), як правило через Three.js або напрямку через WebGL API для нестандартних завдань.

Стек та підходи

Three.js — стандарт де-факто для більшості веб-проектів. Абстрагує шейдери та буфери, надає сцену, камеру, освітлення. Версія r169+ підтримує WebGPU як альтернативний рендерер.

Raw WebGL застосовується коли потрібна повна контрольованість: кастомні геометричні примітиви, нестандартні режими змішування, мінімальний розмір bundle без зайвого коду.

GLSL-шейдери пишуться вручну під кожен ефект — універсальних рішень тут не існує.

// Вертексний шейдер — деформація площини за шумом
uniform float uTime;
uniform float uAmplitude;

varying vec2 vUv;

// Simplex noise (вбудовується як функція)
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }

void main() {
  vUv = uv;

  vec3 pos = position;
  float noise = snoise(vec2(pos.x * 0.5 + uTime * 0.3, pos.y * 0.5));
  pos.z += noise * uAmplitude;

  gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
// Three.js — ініціалізація сцени з постпроцесингом
import * as THREE from 'three'
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'

const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector('#webgl'),
  antialias: true,
  alpha: true,
})
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.toneMapping = THREE.ACESFilmicToneMapping

const composer = new EffectComposer(renderer)
composer.addPass(new RenderPass(scene, camera))
composer.addPass(new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  0.8,  // strength
  0.4,  // radius
  0.85  // threshold
))

Типові ефекти та їх реалізація

Шейдерний фон з шумом

Один з найбільш затребуваних ефектів: анімований градієнтний фон, який реагує на мишу. Реалізується через PlaneGeometry, яка покриває весь viewport, з фрагментним шейдером на базі FBM (fractional Brownian motion).

// Фрагментний шейдер — колірний шум
uniform float uTime;
uniform vec2 uMouse;
uniform vec2 uResolution;

varying vec2 vUv;

void main() {
  vec2 uv = vUv;
  vec2 mouse = uMouse / uResolution;

  // Зміщення UV за позицією миші
  uv += (mouse - 0.5) * 0.05;

  float noise = fbm(uv * 3.0 + uTime * 0.15);

  vec3 colorA = vec3(0.1, 0.0, 0.4);
  vec3 colorB = vec3(0.0, 0.3, 0.8);
  vec3 colorC = vec3(0.8, 0.1, 0.3);

  vec3 color = mix(colorA, colorB, noise);
  color = mix(color, colorC, smoothstep(0.4, 0.7, noise));

  gl_FragColor = vec4(color, 1.0);
}

Система частинок

Для 100k+ частинок використовується BufferGeometry з атрибутами в Float32Array. Анімація йде цілком у вертексному шейдері — CPU не задіяний при runtime.

const COUNT = 150000
const positions = new Float32Array(COUNT * 3)
const randoms = new Float32Array(COUNT)

for (let i = 0; i < COUNT; i++) {
  positions[i * 3 + 0] = (Math.random() - 0.5) * 10
  positions[i * 3 + 1] = (Math.random() - 0.5) * 10
  positions[i * 3 + 2] = (Math.random() - 0.5) * 10
  randoms[i] = Math.random()
}

const geometry = new THREE.BufferGeometry()
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
geometry.setAttribute('aRandom', new THREE.BufferAttribute(randoms, 1))

const material = new THREE.ShaderMaterial({
  uniforms: {
    uTime: { value: 0 },
    uSize: { value: 3.0 * renderer.getPixelRatio() },
  },
  vertexShader: particleVertexShader,
  fragmentShader: particleFragmentShader,
  transparent: true,
  depthWrite: false,
  blending: THREE.AdditiveBlending,
})

Image distortion при наведенні

Текстура зображення завантажується як THREE.Texture, деформується через displacement map за позицією курсора. Ефект «рідкого» наведення.

// Uniforms для передачі в шейдер
const uniforms = {
  uTexture: { value: texture },
  uDisplacement: { value: displacementTexture },
  uMouse: { value: new THREE.Vector2(0, 0) },
  uVelo: { value: 0 },
}

// Відстеження швидкості руху миші
let lastMouse = new THREE.Vector2()
let currentVelo = 0

window.addEventListener('mousemove', (e) => {
  const current = new THREE.Vector2(
    e.clientX / window.innerWidth,
    1.0 - e.clientY / window.innerHeight
  )
  const delta = current.distanceTo(lastMouse)
  currentVelo = Math.min(delta * 10, 1.0)
  lastMouse.copy(current)
  uniforms.uMouse.value.copy(current)
})

Інтеграція з React

Через @react-three/fiber (R3F) Three.js вбудовується у React-компонент декларативно. @react-three/drei дає готові хелпери: useGLTF, MeshTransmissionMaterial, Float, Environment.

import { Canvas, useFrame } from '@react-three/fiber'
import { useRef } from 'react'
import * as THREE from 'three'

function AnimatedMesh() {
  const meshRef = useRef<THREE.Mesh>(null)

  useFrame(({ clock, pointer }) => {
    if (!meshRef.current) return
    meshRef.current.rotation.y = clock.getElapsedTime() * 0.3
    meshRef.current.position.x = THREE.MathUtils.lerp(
      meshRef.current.position.x,
      pointer.x * 2,
      0.05
    )
  })

  return (
    <mesh ref={meshRef}>
      <icosahedronGeometry args={[1.5, 4]} />
      <meshStandardMaterial
        color="#5500ff"
        wireframe={false}
        roughness={0.1}
        metalness={0.8}
      />
    </mesh>
  )
}

export function Scene() {
  return (
    <Canvas
      camera={{ position: [0, 0, 5], fov: 45 }}
      gl={{ antialias: true, alpha: true }}
      dpr={[1, 2]}
    >
      <ambientLight intensity={0.5} />
      <pointLight position={[10, 10, 10]} intensity={1} />
      <AnimatedMesh />
    </Canvas>
  )
}

Продуктивність

Target framerate — 60fps на десктопі, 30fps на мобілях з автоматичним зниженням якості. Визначається через navigator.hardwareConcurrency та бенчмарк при першому рендері.

Ключові правила:

  • Один drawcall замість тисячі: InstancedMesh для повторюваної геометрії
  • renderer.setPixelRatio(Math.min(devicePixelRatio, 2)) — не рендерити на 3x на Retina без потреби
  • Dispose при unmount: geometry.dispose(), material.dispose(), texture.dispose()
  • requestAnimationFrame через Three.js renderer, не свій loop
  • Постпроцесинг тільки при prefersReducedMotion === false
// Перевірка перед ініціалізацією важких ефектів
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches
const isMobile = /Mobi|Android/i.test(navigator.userAgent)

const config = {
  bloomEnabled: !prefersReduced && !isMobile,
  particleCount: isMobile ? 10000 : 150000,
  pixelRatio: isMobile ? 1 : Math.min(devicePixelRatio, 2),
}

Завантаження ассетів

3D-моделі — формат .glb (GLB = бінарний GLTF). Стиснення через Draco (geom) + KTX2 (текстури). Завантаження через GLTFLoader + DRACOLoader.

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'

const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('/draco/')  // wasm в public/

const ktx2Loader = new KTX2Loader()
ktx2Loader.setTranscoderPath('/basis/')
ktx2Loader.detectSupport(renderer)

const loader = new GLTFLoader()
loader.setDRACOLoader(dracoLoader)
loader.setKTX2Loader(ktx2Loader)

loader.load('/models/scene.glb', (gltf) => {
  scene.add(gltf.scene)
}, (progress) => {
  const pct = (progress.loaded / progress.total * 100).toFixed(0)
  onProgress(pct)
})

Строки та етапи

Прототип з основним ефектом — 3–5 днів. Повна інтеграція в сайт з адаптивністю, fallback для слабких пристроїв, оптимізація bundle — 10–20 днів залежно від складності сцени. Анімований hero з шейдерним фоном та реакцією на мишу — ближче до нижної межи. Інтерактивна 3D-модель продукту з конфігуратором матеріалів — до верхньої.