WebGL Visualization Implementation for Website

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1215
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1043
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    815

WebGL Visualization Implementation on Website

WebGL is direct access to the GPU from the browser. This is not about 3D models — it's about data visualization with hundreds of thousands of points, procedural graphics, particle systems, custom shaders. Canvas 2D and SVG can't handle large data: thousands of DOM nodes kill performance. WebGL renders a million points in a single draw call.

When WebGL is Needed

  • Scatter plot with 500,000+ points (financial data, geospatial)
  • Particle systems: interactive backgrounds, physics process visualizations
  • Real-time heatmaps (trading data, click heatmaps)
  • Procedural animations (Perlin noise, mathematical surfaces)
  • Image processing through shaders (filters, effects)

For standard charts (100–10,000 points) — D3.js or Recharts are sufficient.

deck.gl: Geospatial Data Visualization

deck.gl from Uber — WebGL library for maps and large datasets.

npm install deck.gl @deck.gl/layers @deck.gl/react react-map-gl maplibre-gl
import DeckGL from '@deck.gl/react'
import { ScatterplotLayer, HeatmapLayer, ColumnLayer } from '@deck.gl/layers'
import Map from 'react-map-gl/maplibre'

interface DataPoint {
  coordinates: [number, number]
  value: number
  category: string
}

function GeoVisualization({ data }: { data: DataPoint[] }) {
  const [viewState, setViewState] = useState({
    longitude: 37.6,
    latitude: 55.75,
    zoom: 10,
    pitch: 45,
    bearing: 0,
  })

  const layers = [
    new ScatterplotLayer({
      id: 'scatter',
      data,
      getPosition: (d) => d.coordinates,
      getRadius: (d) => Math.sqrt(d.value) * 10,
      getFillColor: (d) => {
        // Color encoding by value
        const t = d.value / 1000
        return [255 * t, 100, 255 * (1 - t), 200]
      },
      pickable: true,
      radiusMinPixels: 2,
      radiusMaxPixels: 50,
    }),

    new HeatmapLayer({
      id: 'heatmap',
      data,
      getPosition: (d) => d.coordinates,
      getWeight: (d) => d.value,
      radiusPixels: 40,
      intensity: 1,
      threshold: 0.1,
      colorRange: [
        [0, 0, 255, 0],
        [0, 255, 255, 128],
        [0, 255, 0, 200],
        [255, 255, 0, 220],
        [255, 0, 0, 255],
      ],
    }),
  ]

  return (
    <DeckGL
      viewState={viewState}
      onViewStateChange={({ viewState }) => setViewState(viewState as any)}
      layers={layers}
      getTooltip={({ object }: { object: DataPoint }) =>
        object && { html: `<b>Value:</b> ${object.value}`, style: { background: '#fff' } }
      }
      style={{ position: 'relative', height: '600px' }}
      controller={true}
    >
      <Map
        mapStyle="https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"
      />
    </DeckGL>
  )
}

WebGL Shaders Directly: GLSL

For full control — write vertex and fragment shaders:

function WebGLCanvas() {
  const canvasRef = useRef<HTMLCanvasElement>(null)

  useEffect(() => {
    const canvas = canvasRef.current!
    const gl = canvas.getContext('webgl2')!

    const vertexShaderSrc = `#version 300 es
      in vec2 a_position;
      in float a_value;
      out float v_value;
      uniform vec2 u_resolution;

      void main() {
        vec2 zeroToOne = a_position / u_resolution;
        vec2 zeroToTwo = zeroToOne * 2.0;
        vec2 clipSpace = zeroToTwo - 1.0;
        gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
        gl_PointSize = max(2.0, sqrt(a_value) * 3.0);
        v_value = a_value;
      }
    `

    const fragmentShaderSrc = `#version 300 es
      precision highp float;
      in float v_value;
      out vec4 outColor;

      vec3 viridis(float t) {
        const vec3 c0 = vec3(0.267, 0.004, 0.329);
        const vec3 c1 = vec3(0.127, 0.566, 0.551);
        const vec3 c2 = vec3(0.993, 0.906, 0.144);
        return mix(mix(c0, c1, t), mix(c1, c2, t), t);
      }

      void main() {
        vec2 coord = gl_PointCoord - 0.5;
        if (length(coord) > 0.5) discard;

        outColor = vec4(viridis(v_value), 0.8);
      }
    `

    function createShader(type: number, source: string): WebGLShader {
      const shader = gl.createShader(type)!
      gl.shaderSource(shader, source)
      gl.compileShader(shader)
      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        throw new Error(gl.getShaderInfoLog(shader) ?? 'Shader error')
      }
      return shader
    }

    const program = gl.createProgram()!
    gl.attachShader(program, createShader(gl.VERTEX_SHADER, vertexShaderSrc))
    gl.attachShader(program, createShader(gl.FRAGMENT_SHADER, fragmentShaderSrc))
    gl.linkProgram(program)

    // Generate 100,000 points
    const N = 100_000
    const positions = new Float32Array(N * 2)
    const values = new Float32Array(N)

    for (let i = 0; i < N; i++) {
      positions[i * 2] = Math.random() * canvas.width
      positions[i * 2 + 1] = Math.random() * canvas.height
      values[i] = Math.random()
    }

    // Load data to GPU
    const posBuffer = gl.createBuffer()
    gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)

    const aPosition = gl.getAttribLocation(program, 'a_position')
    gl.enableVertexAttribArray(aPosition)
    gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0)

    const valBuffer = gl.createBuffer()
    gl.bindBuffer(gl.ARRAY_BUFFER, valBuffer)
    gl.bufferData(gl.ARRAY_BUFFER, values, gl.STATIC_DRAW)

    const aValue = gl.getAttribLocation(program, 'a_value')
    gl.enableVertexAttribArray(aValue)
    gl.vertexAttribPointer(aValue, 1, gl.FLOAT, false, 0, 0)

    gl.useProgram(program)
    gl.uniform2f(gl.getUniformLocation(program, 'u_resolution'), canvas.width, canvas.height)

    gl.clearColor(0.05, 0.05, 0.1, 1)
    gl.clear(gl.COLOR_BUFFER_BIT)
    gl.enable(gl.BLEND)
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

    // Render 100,000 points in one draw call
    gl.drawArrays(gl.POINTS, 0, N)
  }, [])

  return <canvas ref={canvasRef} width={800} height={600} />
}

Regl: Convenient WebGL Wrapper

npm install regl
npm install -D @types/regl
import createREGL from 'regl'

const regl = createREGL(canvasRef.current!)

// Particle system
const drawParticles = regl({
  vert: `
    precision mediump float;
    attribute vec2 position;
    attribute float age;
    uniform float time;
    void main() {
      vec2 pos = position + vec2(cos(time + age), sin(time * 0.7 + age)) * 0.05;
      gl_Position = vec4(pos, 0, 1);
      gl_PointSize = 3.0;
    }
  `,
  frag: `
    precision mediump float;
    void main() {
      gl_FragColor = vec4(0.4, 0.8, 1.0, 0.7);
    }
  `,
  attributes: {
    position: particlePositions,
    age: particleAges,
  },
  uniforms: {
    time: regl.context('time'),
  },
  count: PARTICLE_COUNT,
  primitive: 'points',
})

regl.frame(({ time }) => {
  regl.clear({ color: [0, 0, 0.1, 1], depth: 1 })
  drawParticles()
})

What We Do

Analyze the data volume and visualization type: geospatial data — deck.gl, particle systems and custom shaders — raw WebGL or regl, scatter plots on maps — MapLibre + deck.gl. Optimize for 60 fps, test on mid-range mobile devices.

Timeline: basic WebGL visualization with ready-made library — 3–4 days. Custom shaders and complex particle systems — 7–10 days.