Skip to content

<LinearGradientTexture>

A reactive linear gradient texture. The underlying texture uses an OffscreenCanvas and a CanvasTexture and is assigned the same colorspace as the renderer.

<script lang="ts">
import Scene from './Scene.svelte'
import type { ToneMapping, Wrapping } from 'three'
import { Canvas } from '@threlte/core'
import { Color, Folder, List, Pane, Slider } from 'svelte-tweakpane-ui'
import {
ClampToEdgeWrapping,
MirroredRepeatWrapping,
RepeatWrapping,
ACESFilmicToneMapping,
AgXToneMapping,
CineonToneMapping,
LinearToneMapping,
NeutralToneMapping,
NoToneMapping,
ReinhardToneMapping
} from 'three'
const toneMappingOptions: Record<PropertyKey, ToneMapping> = {
ACESFilmic: ACESFilmicToneMapping,
AgX: AgXToneMapping,
Cineon: CineonToneMapping,
Linear: LinearToneMapping,
NeutralToneMapping,
None: NoToneMapping,
Reinhard: ReinhardToneMapping
}
const wrappingOptions: Record<PropertyKey, Wrapping> = {
ClampToEdge: ClampToEdgeWrapping,
MirroredRepeat: MirroredRepeatWrapping,
Repeat: RepeatWrapping
}
const canvasSize = 1024
let sceneClearColor = $state('#000000')
let sceneToneMapping = $state(AgXToneMapping)
let gradientStartColor = $state('#ff00ff')
let gradientEndColor = $state('#ffff00')
let gradientStartX = $state(0)
let gradientStartY = $state(0)
let gradientEndX = $state(0)
let gradientEndY = $state(canvasSize)
let textureCenterX = $state(0)
let textureCenterY = $state(0)
let textureOffsetX = $state(0)
let textureOffsetY = $state(0)
let textureRepeatX = $state(1)
let textureRepeatY = $state(1)
let textureRotationDegrees = $state(0)
let textureWrapS = $state<Wrapping>(ClampToEdgeWrapping)
let textureWrapT = $state<Wrapping>(ClampToEdgeWrapping)
let textureRotation = $derived((Math.PI / 180) * textureRotationDegrees)
</script>
<Pane position="fixed">
<List
bind:value={sceneToneMapping}
options={toneMappingOptions}
label="tone mapping"
/>
<Color
bind:value={sceneClearColor}
label="clear color"
/>
<Folder title="gradient props">
<Color
bind:value={gradientStartColor}
label="start color"
/>
<Color
bind:value={gradientEndColor}
label="end color"
/>
<Slider
bind:value={gradientStartX}
label="start x"
min={0}
max={canvasSize}
step={1}
/>
<Slider
bind:value={gradientStartY}
label="start y"
min={0}
max={canvasSize}
step={1}
/>
<Slider
bind:value={gradientEndX}
label="end x"
min={0}
max={canvasSize}
step={1}
/>
<Slider
bind:value={gradientEndY}
label="end y"
min={0}
max={canvasSize}
step={1}
/>
</Folder>
<Folder title="texture props">
<List
bind:value={textureWrapS}
label="wrapS"
options={wrappingOptions}
/>
<List
bind:value={textureWrapT}
label="wrapT"
options={wrappingOptions}
/>
<Slider
label="centerX"
bind:value={textureCenterX}
min={-0.5}
max={1.5}
step={0.5}
/>
<Slider
label="centerY"
bind:value={textureCenterY}
min={-0.5}
max={1.5}
step={0.5}
/>
<Slider
label="offsetX"
bind:value={textureOffsetX}
min={-2}
max={2}
step={1}
/>
<Slider
label="offsetY"
bind:value={textureOffsetY}
min={-2}
max={2}
step={1}
/>
<Slider
label="repeatX"
bind:value={textureRepeatX}
min={0}
max={5}
step={1}
/>
<Slider
label="repeatY"
bind:value={textureRepeatY}
min={0}
max={5}
step={1}
/>
<Slider
label="rotation (degrees)"
bind:value={textureRotationDegrees}
min={-360}
max={360}
step={1}
/>
</Folder>
</Pane>
<div>
<Canvas>
<Scene
{gradientEndColor}
{gradientEndX}
{gradientEndY}
{gradientStartColor}
{gradientStartX}
{gradientStartY}
{sceneClearColor}
{sceneToneMapping}
{textureCenterX}
{textureCenterY}
{textureOffsetX}
{textureOffsetY}
{textureRepeatX}
{textureRepeatY}
{textureRotation}
{textureWrapS}
{textureWrapT}
{canvasSize}
/>
</Canvas>
</div>
<style>
div {
height: 100%;
}
</style>
<script lang="ts">
import type { ColorRepresentation, ToneMapping, Wrapping } from 'three'
import type { ColorStop } from '@threlte/extras'
import { DoubleSide } from 'three'
import { LinearGradientTexture, OrbitControls } from '@threlte/extras'
import { T, useThrelte } from '@threlte/core'
type SceneProps = {
canvasSize: number
gradientEndColor: string
gradientEndX: number
gradientEndY: number
gradientStartColor: string
gradientStartX: number
gradientStartY: number
sceneClearColor: ColorRepresentation
sceneToneMapping: ToneMapping
textureCenterX: number
textureCenterY: number
textureOffsetX: number
textureOffsetY: number
textureRepeatX: number
textureRepeatY: number
textureRotation: number
textureWrapS: Wrapping
textureWrapT: Wrapping
}
let {
canvasSize,
gradientEndColor,
gradientEndX,
gradientEndY,
gradientStartColor,
gradientStartX,
gradientStartY,
sceneClearColor,
sceneToneMapping,
textureCenterX,
textureCenterY,
textureOffsetX,
textureOffsetY,
textureRepeatX,
textureRepeatY,
textureRotation,
textureWrapS,
textureWrapT
}: SceneProps = $props()
let stops = $derived<ColorStop[]>([
{ color: gradientStartColor, offset: 0 },
{ color: gradientEndColor, offset: 1 }
])
const { invalidate, renderer, toneMapping } = useThrelte()
$effect(() => {
toneMapping.set(sceneToneMapping)
invalidate()
})
$effect(() => {
renderer.setClearColor(sceneClearColor)
invalidate()
})
</script>
<T.PerspectiveCamera
makeDefault
position.z={5}
>
<OrbitControls />
</T.PerspectiveCamera>
<T.Mesh scale={2}>
<T.PlaneGeometry />
<T.MeshBasicMaterial side={DoubleSide}>
<LinearGradientTexture
width={canvasSize}
height={canvasSize}
startX={gradientStartX}
startY={gradientStartY}
endX={gradientEndX}
endY={gradientEndY}
center.x={textureCenterX}
center.y={textureCenterY}
offset.x={textureOffsetX}
offset.y={textureOffsetY}
repeat.x={textureRepeatX}
repeat.y={textureRepeatY}
rotation={textureRotation}
wrapS={textureWrapS}
wrapT={textureWrapT}
{stops}
/>
</T.MeshBasicMaterial>
</T.Mesh>

The texture is automatically attached to the map property of its parent. You can disable this behaviour by setting the attach prop to false. This may be useful if you want to create the texture but use it somewhere else.

<script>
let texture = $state()
</script>
<LinearGradientTexture
attach={false}
bind:ref={texture}
/>
<SomeComponent {texture} />

<LinearGradientTexture> accepts a stops prop which is an array of color stops that define the gradient. A stop is defined by two things; an offset and a color. Gradient stops are identical to how you would use them with a CanvasRenderingContext2D, notably the offset should be a number between 0 and 1 inclusive. Stop colors can be any valid color representation in Three.js. Here are a couple examples of valid stops.

<LinearGradientTexture
stops={[
{ color: 'black', offset: 0 },
{ color: 'white', offset: 1 }
]}
/>
<LinearGradientTexture
stops={[
{ color: '#00ffff', offset: 0 },
{ color: '#ff00ff', offset: 0.5 },
{ color: '#ffff00', offset: 1 }
]}
/>

You can even mix and match color representations

<LinearGradientTexture
stops={[
{ color: 'red', offset: 0 },
{ color: 0xff_00_00, offset: 0.25 },
{ color: 'rgb(255, 0, 0)', offset: 0.5 },
{ color: '#ff0000', offset: 0.75 },
{ color: new Color(new Color(new Color())).set(1, 0, 0), offset: 1 }
]}
/>

All of the colors above are valid representations of the color red.

You can control the gradient start point and end point with the startX, startY, endX, and endY props. For example, the props for a gradient that starts at the bottom left corner of the texture and ends at the top right corner would be:

<LinearGradientTexture
startX={0}
startY={height}
endX={width}
endY={0}
/>

If the colors in your scene do not match the color in your stops, you may need to adjust the tone mapping of the scene. ToneMapping constants are imported from the three library.

<script>
import { useThrelte } from '@threlte/core'
import { LinearToneMapping } from 'three'
const { toneMapping } = useThrelte()
toneMapping.set(LinearToneMapping)
</script>

<LinearGradientTexture> extends < T . CanvasTexture > and supports all its props, snippets, bindings and events.

Props

name
type
required
default
description

endX
number
no
0
x-axis coordinate of the gradient's end point

endY
number
no
height
y-axis coordinate of the gradient's end point

height
number
no
1024
height of the texture's canvas

startX
number
no
0
x-axis coordinate of the gradient's start point

startY
number
no
0
y-axis coordinate of the gradient's start point

stops
ColorStop[]
no
[{color: '#000000', offset: 0}, {color: '#ffffff', offset: 1}]
list of stops applied to the gradient. more info at https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createLinearGradient

width
number
no
1024
width of the texture's canvas