import {DeviceTabs} from '@site/src/react-luma'; import {HelloTriangleExample} from '@site'; # Hello Triangle This tutorial will demonstrate how to draw a triangle using luma.gl's high-level APIs. It is assumed you've set up your development environment as described in [Getting Started](/docs/getting-started). Your `index.js` file should look like the following: ```typescript import {AnimationLoop} from '@luma.gl/engine'; import {clear} from '@luma.gl/webgl'; class AppAnimationLoop extends AnimationLoop ({ override onInitialize({device}) { // Setup logic goes here }, override onRender({device}) { // Drawing logic goes here clear(device, {color: [0, 0, 0, 1]}); } }); const loop = new AppAnimationLoop(); loop.start(); ``` First, we'll need to update our imports with the classes we'll be using, `Buffer` and `Model`: ```typescript import {AnimationLoop, Model} from '@luma.gl/engine'; import {clear} from '@luma.gl/webgl'; ``` Now let's create some buffers in the `onInitialize` method to hold our attribute data: ```typescript override onInitialize({device}) { // Setup logic goes here const positionBuffer = device.createBuffer(new Float32Array([ -0.5, -0.5, 0.5, -0.5, 0.0, 0.5 ])); const colorBuffer = device.createBuffer(new Float32Array([ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ])); } ``` Next let's add the vertex and fragment shader code we'll be using to draw: ```typescript override onInitialize({device}) { // Setup logic goes here // Buffers... const vs = ` attribute vec2 position; attribute vec3 color; varying vec3 vColor; void main() { vColor = color; gl_Position = vec4(position, 0.0, 1.0); } `; const fs = ` varying vec3 vColor; void main() { gl_FragColor = vec4(vColor, 1.0); } `; } ``` As a final step in our initialization, we'll create a `Model` in `onInitialize`: ```typescript override onInitialize({device}) { // Setup logic goes here // Buffers... // Shaders... this.model = new Model(device, { vs, fs, attributes: { position: positionBuffer, color: colorBuffer }, vertexCount: 3 }); return {model}; } ``` A `Model` can be thought of as gathering all the WebGL pieces necessary for a single draw call: programs, attributes, uniforms. Also note that we return the `Model` instance we created. This will make it available to the `onRender` method. Our `onRender` method is comparitavely much simpler: ```typescript override onRender({device}) { clear(device, {color: [0, 0, 0, 1]}); this.model.draw(); } ``` This clears the canvas and draws the `Model`. If all went well, you should see a tri-color triangle on a black background. The entire application should look like the following: ```typescript import {AnimationLoop, Model} from '@luma.gl/engine'; import {clear} from '@luma.gl/webgl'; class AppAnimationLoop extends AnimationLoop { override onInitialize({device}) { const positionBuffer = device.createBuffer(new Float32Array([-0.5, -0.5, 0.5, -0.5, 0.0, 0.5])); const colorBuffer = device.createBuffer(new Float32Array([1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0])); const vs = ` attribute vec2 position; attribute vec3 color; varying vec3 vColor; void main() { vColor = color; gl_Position = vec4(position, 0.0, 1.0); } `; const fs = ` varying vec3 vColor; void main() { gl_FragColor = vec4(vColor, 1.0); } `; const model = new Model(device, { vs, fs, attributes: { position: positionBuffer, color: colorBuffer }, vertexCount: 3 }); return {model}; }, override onRender({device, model}) { clear(device, {color: [0, 0, 0, 1]}); model.draw(); } }; const loop = new AppAnimationLoop(); loop.start(); ```