Atlaslog
Atlaslog / luma.gl / Tutorial / AnimationLoop and time-driven rendering
luma.gl · Tutorial

AnimationLoop and time-driven rendering

Quick answer

This tutorial covers luma.gl's AnimationLoop, which waits for the Device, invokes a render callback each frame and releases resources on stop. It wires onInitialize, onRender, onFinalize and onError around an AnimationProps object exposing device, width, height, time, tick and tock, and builds a Model with a Buffer written each frame. It then adds setNeedsRedraw for static scenes. Readers can run a frame-rate independent, resource-clean render loop.

Last verified 2026-11-03 · luma.gl 9.4 · Cite this page
Key points
Example
(async function () {
  const device = await luma.luma.createDevice({
    type: 'webgl',
    adapters: [luma.webgl2Adapter],
    createCanvasContext: {
      canvas: document.getElementById('cv'),
      useDevicePixels: Math.min(window.devicePixelRatio, 2),
      autoResize: true
    }
  });

  let model = null;
  let positions = null;

  const loop = new luma.AnimationLoop({
    device: device,

    onInitialize: function (props) {
      positions = device.createBuffer({
        data: new Float32Array(3 * 512),
        usage: luma.Buffer.VERTEX | luma.Buffer.COPY_DST
      });

      model = new luma.Model(device, {
        vs: vsSource,
        fs: fsSource,
        attributes: { positions: positions },
        bufferLayout: [
          { name: 'positions', stepMode: 'vertex', format: 'float32x3' }
        ],
        vertexCount: 512,
        topology: 'line-strip'
      });

      console.log('初始画布尺寸', props.width, props.height);
    },

    onRender: function (props) {
      const seconds = (props.time - props.startTime) / 1000;

      if (props.tick % 2 === 0) {
        const data = new Float32Array(3 * 512);
        for (let i = 0; i < 512; i++) {
          data[i * 3] = (i / 511) * 2 - 1;
          data[i * 3 + 1] = Math.sin(i * 0.05 + seconds) * 0.4;
          data[i * 3 + 2] = 0;
        }
        positions.write(data);
      }

      const pass = props.device.beginRenderPass({
        clearColor: [0.05, 0.07, 0.10, 1]
      });
      model.draw(pass);
      pass.end();
    },

    onFinalize: function () {
      model.destroy();
      positions.destroy();
    },

    onError: function (error) {
      console.error('渲染出错', error);
    }
  });

  await loop.start();
})();
const loop = new luma.AnimationLoop({
  device: device,
  onRender: function (props) {
    if (!loop.needsRedraw()) return;

    const pass = props.device.beginRenderPass({ clearColor: [0, 0, 0, 1] });
    model.draw(pass);
    pass.end();
  }
});
map.on('move', function () {
  updateViewMatrix();
  loop.setNeedsRedraw('camera-changed');
});
Notes
Runnable examplesChinese originalAll Tutorials
English edition written for Atlaslog. Code is taken from the original Chinese page and runs unchanged.
Cite this page

Atlaslog (2026). AnimationLoop and time-driven rendering. https://www.jingweizhi.com/en/lumagl/tutorials/animation-loop.html (accessed 2026-11-03).