threlte logo
@threlte/extras

<Grid>

A robust grid implementation with multiple tweakable parameters.

<script lang="ts">
	import { Canvas } from '@threlte/core'
	import { Grid } from '@threlte/extras'
	import { useTweakpane } from '$lib/useTweakpane'
	import Scene from './Scene.svelte'

	const { pane, action, addInput } = useTweakpane({
		title: 'Grid',
		expanded: false
	})

	const cellFolder = pane.addFolder({
		title: 'Cell settings'
	})
	const cellSize = addInput({
		label: 'Cell size',
		value: 1,
		params: {
			step: 1,
			min: 1,
			max: 5
		},
		parent: cellFolder
	})
	const cellColor = addInput({
		label: 'Cell color',
		value: `#ffffff`,
		parent: cellFolder
	})

	const cellThickness = addInput({
		label: 'Cell thickness',
		value: 1.4,
		params: {
			step: 0.1,
			min: 1,
			max: 10
		},
		parent: cellFolder
	})

	const sectionFolder = pane.addFolder({
		title: 'Section settings'
	})

	const sectionSize = addInput({
		label: 'Section size',
		value: 5,
		params: {
			step: 1,
			min: 1,
			max: 50
		},
		parent: sectionFolder
	})
	const sectionColor = addInput({
		label: 'Section color',
		value: `#FF3E00`,
		parent: sectionFolder
	})
	const sectionThickness = addInput({
		label: 'Section thickness',
		value: 2,
		params: {
			step: 0.1,
			min: 1,
			max: 10
		},

		parent: sectionFolder
	})

	const generalFolder = pane.addFolder({
		title: 'General settings'
	})

	const gridSize1 = addInput({
		label: 'Grid size1',
		value: 20,
		params: {
			step: 1,
			min: 1,
			max: 100
		},

		parent: generalFolder
	})
	const gridSize2 = addInput({
		label: 'Grid size2',
		value: 20,
		params: {
			step: 1,
			min: 1,
			max: 100
		},
		parent: generalFolder
	})

	const axes = addInput({
		label: 'axes',
		value: 'xzy',
		params: {
			options: {
				xzy: 'xzy',
				xyz: 'xyz',
				zyx: 'zyx'
			}
		},
		parent: generalFolder
	})
	$: axisTyped = $axes as 'xzy' | 'xyz' | 'zyx'

	const followCamera = addInput({
		label: 'followCamera',
		value: false,

		parent: generalFolder
	})

	const infiniteGrid = addInput({
		label: 'infiniteGrid',
		value: false,
		parent: generalFolder
	})

	const fadeDistance = addInput({
		label: 'fadeDistance',
		value: 100,
		params: {
			step: 10,
			min: 10,
			max: 400
		},

		parent: generalFolder
	})

	const fadeStregth = addInput({
		label: 'fadeStregth',
		value: 1,
		params: {
			step: 0.1,
			min: 0,
			max: 20
		},

		parent: generalFolder
	})
</script>

<div use:action />

<div class="relative h-full w-full bg-orange-500/20">
	<Canvas>
		<Grid
			axes={axisTyped}
			cellColor={$cellColor}
			cellSize={$cellSize}
			cellThickness={$cellThickness}
			sectionColor={$sectionColor}
			sectionSize={$sectionSize}
			sectionThickness={$sectionThickness}
			followCamera={$followCamera}
			infiniteGrid={$infiniteGrid}
			fadeDistance={$fadeDistance}
			fadeStrength={$fadeStregth}
			gridSize={[$gridSize1, $gridSize2]}
		/>

		<!-- Example scene with boxes -->
		<Scene />
	</Canvas>
</div>
<script lang="ts">
  import { T } from '@threlte/core'
  import { OrbitControls } from '@threlte/extras'
  import { BoxGeometry } from 'three'
  const boxColors = [
    '#3c42c4',
    '#6e51c8',
    '#a065cd',
    '#ce79d2',
    '#d68fb8',
    '#dda2a3',
    '#eac4ae',
    '#f4dfbe'
  ]
</script>

<T.PerspectiveCamera
  makeDefault
  position={[0, 10, 20]}
  fov={36}
  target={[0, 0, 0]}
>
  <OrbitControls />
</T.PerspectiveCamera>

<!-- Make a box in every second cell to show aligment -->

{#each { length: 10 } as _h, x}
  {#each { length: 10 } as _v, y}
    {#if x % 2 == 0 && y % 2 == 0}
      <T.Group position={[x - 4.5, 0.5, y - 4.5]}>
        <T.Mesh>
          <T.BoxGeometry />
          <T.MeshBasicMaterial
            args={[
              {
                color: boxColors[Math.floor(Math.random() * boxColors.length)],
                opacity: 0.9,
                transparent: true
              }
            ]}
          />
        </T.Mesh>
        <T.LineSegments>
          <T.EdgesGeometry args={[new BoxGeometry()]} />

          <T.MeshBasicMaterial
            args={[
              {
                color: 0x000000
              }
            ]}
          />
        </T.LineSegments>
      </T.Group>
    {/if}
  {/each}
{/each}

Usage

This component provides sensible defaults. You can initialize the default grid with just <Grid>.

Cells and Sections

Grid is split into cells and sections. Cell is meant to represent the smallest units on your grid, whereas section is a group of cells. You can adjust the size of the grid by changing the cellSize and sectionSize parameters. Size is in Three world units, so for example a mesh with BoxGeometry(1,1,1) will fit perfectly into a size 1 cell. By default a cell is 1 unit and a section 10, which means that a grid of 10x10 cells will be outlined with a section line.

Lines

You can adjust the color and thickness of cell and section lines with cellColor, cellThickness, sectionColor, sectionThickness.

Grid size and fading

The <Grid> component is a THREE.Mesh with a PlaneGeometry attached to it. The gridSize parameter defines the size of the PlaneGeometry. You can extend the grid into infinity if you set the infiniteGrid parameter to true. Changing fadeDistance sets how far from the camera position the grid begins to fade by having its alpha reduced. fadeStrength determines how fast it happens (exponent). fadeStrength = 0 means that there is no fading (not recommended for large grids).

Follow camera

Setting followCamera to true applies a transform that moves the grid to the camera’s position on the chosen axes.

ref passes a reference from the <T.Mesh/> the grid is constructed on.

Component Signature

<Grid> extends <T.Mesh> and supports all its props, slot props, bindings and events.

Props

name
type
required
default

axes
'xzy' | 'xyz' | 'zyx'
no
'xzy'

cellColor
ColorRepresentation
no
'#000000'

cellSize
number
no
1

cellThickness
number
no
1

fadeDistance
number
no
100

fadeStrength
number
no
1

followCamera
boolean
no
false

gridSize
number | [number, number]
no
[20,20]

infiniteGrid
boolean
no
false

sectionColor
ColorRepresentation
no
'#0000ee'

sectionSize
number
no
10

sectionThickness
number
no
2