@threlte/extras
<AsciiRenderer>
A wrapper around Three’s AsciiEffect addon. It replaces the main render function with a function that renders the scene to an HTML table and overlays it on top of the canvas. Areas with a higher “brightness” are mapped to characters that appear “fuller”.
<script lang="ts">
import Scene from './Scene.svelte'
import type { AsciiEffectOptions } from 'three/examples/jsm/Addons.js'
import { AsciiRenderer } from '@threlte/extras'
import { Button, Checkbox, Color, Folder, Pane, Slider, Text } from 'svelte-tweakpane-ui'
import { Canvas } from '@threlte/core'
let fgColor = $state('#ff2400')
let bgColor = $state('#000000')
const defaultCharacters = ' .:-+*=%@#'
let characters = $state(defaultCharacters)
let alpha = $state(1)
let block = $state(false)
let color = $state(false)
let invert = $state(true)
let resolution = $state(0.1)
let scale = $state(1)
const options: AsciiEffectOptions = $derived({
alpha,
block,
color,
invert,
resolution,
scale
})
let autoRotate = $state(true)
</script>
<div>
<Pane
position="fixed"
title="AsciiRenderer"
>
<Folder title="scene">
<Checkbox
bind:value={autoRotate}
label="auto rotate"
/>
</Folder>
<Folder title="options">
<Slider
bind:value={scale}
label="scale"
min={1}
max={3}
step={1}
/>
<Slider
bind:value={resolution}
label="resolution"
min={0.05}
max={0.2}
step={0.05}
/>
<Checkbox
bind:value={invert}
label="invert"
/>
<Checkbox
bind:value={color}
label="color"
/>
{#if color}
<Checkbox
bind:value={block}
label="block"
/>
{/if}
</Folder>
<Folder title="props">
<Text
bind:value={characters}
label="characters"
/>
<Button
on:click={() => {
characters = defaultCharacters
}}
title="reset characters"
/>
{#if !color}
<Color
bind:value={fgColor}
label="text color"
/>
<Color
bind:value={bgColor}
label="background color"
/>
{/if}
</Folder>
</Pane>
<Canvas>
<AsciiRenderer
{bgColor}
{characters}
{fgColor}
{options}
/>
<Scene {autoRotate} />
</Canvas>
</div>
<style>
div {
height: 100%;
}
</style>
<script lang="ts">
import { OrbitControls } from '@threlte/extras'
import { T } from '@threlte/core'
type SceneProps = { autoRotate: boolean }
let { autoRotate = true }: SceneProps = $props()
</script>
<T.DirectionalLight position={5} />
<T.PerspectiveCamera
makeDefault
position.z={5}
>
<OrbitControls {autoRotate} />
</T.PerspectiveCamera>
<T.Mesh>
<T.MeshNormalMaterial />
<T.TorusKnotGeometry />
</T.Mesh>
Usage
Typically you’d use <AsciiRenderer>
alongside your main Scene
component. <AsciiRenderer>
creates an absolutely postioned table element that is appended to the dom element of the <Canvas>
. You may need to set the wrapping element’s position to relative
<div>
<Canvas>
<AsciiRenderer />
<Scene />
</Canvas>
</div>
<style>
div {
position: relative;
}
</style>
Characters
The characters
prop should be sorted by ascending “opaqueness”.
<AsciiRenderer characters=" #" />
The example above uses a character set with two characters -
and #
.
Colors
By default the renderer sets options.color
to false
and will only use the colors given by the fgColor
and bgColor
props. fgColor
and bgColor
can be any acceptable CSS color string. If your colors contain an alpha component, make sure to set options.alpha
to true
.
If options.color
is set to true
, fgColor
and bgColor
will be ignored and the corresponding color of the scene will be used for each character.
<AsciiRenderer options={{ color: true }} />
Setting options.color
to true
slows down performance. Using a static scene or manually
rendering can help.
Other Options Props
Because the effect doesn’t support dynamically updating options, any time an options
property changes, a new AsciiEffect
is created inside <AsciiRenderer
. For this reason, options
is passed as an object to <AsciiRenderer>
options.block
makes the characters into color blocks. It is only applied ifoptions.color
istrue
.options.invert
inverts thefgColor
andbgColor
colors.options.resolution
controls how detailed the render is.options.scale
controls the scale of the characters. Note that zooming the camera does not control the size of the characters.
A new AsciiEffect
instance must be created anytime options
changes because an effect can not
have it options changed after it has been created. This is a limitation of the AsciiEffect
addon
from ThreeJS.
Disabling the Render Task
If at some point your scene doesn’t need to be rendered because it will no longer update or nothing will change between frames, you can turn off the rendering task by setting the autoRender
prop to false
. This will stop the render task from running which can improve performance. This is especially useful when using options.color
which is known to slow down the renderer.
AsciiRenderer
passes its AsciiEffect
instance to its children
snippet. This allows you to opt out of AsciiEffect
’s renderering task but still use the effect it creates. It can also be used for on-demand rendering.
<script lang="ts">
import Scene from './Scene.svelte'
import type { AsciiEffect } from 'three/examples/jsm/Addons.js'
import type { AsciiEffectOptions } from 'three/examples/jsm/Addons.js'
import { AsciiRenderer } from '@threlte/extras'
import { Canvas } from '@threlte/core'
import { Checkbox, Pane } from 'svelte-tweakpane-ui'
let asciiRenderer: Component | undefined = $state()
let color = $state(false)
const options: AsciiEffectOptions = $derived({ color })
</script>
<Pane
title="render on update"
position="fixed"
>
<Checkbox
bind:value={color}
label="color"
/>
</Pane>
<div>
<Canvas>
<AsciiRenderer
autoRender={false}
bind:this={asciiRenderer}
{options}
>
{#snippet children({ asciiEffect })}
<Scene {asciiEffect} />
{/snippet}
</AsciiRenderer>
</Canvas>
</div>
<style>
div {
height: 100%;
}
</style>
<script lang="ts">
import { T, useThrelte } from '@threlte/core'
import type { AsciiEffect } from 'three/examples/jsm/Addons.js'
let { asciiEffect }: { asciiEffect: AsciiEffect } = $props()
const { autoRender, camera, scene } = useThrelte()
$effect(() => {
const lastAutoRender = autoRender.current
autoRender.set(false)
return () => {
autoRender.set(lastAutoRender)
}
})
// render once
$effect(() => {
asciiEffect.render(scene, camera.current)
})
</script>
<T.DirectionalLight position={5} />
<T.PerspectiveCamera
makeDefault
position.z={5}
/>
<T.Mesh>
<T.MeshNormalMaterial />
<T.TorusKnotGeometry />
</T.Mesh>
The example above demonstrates how to render on demand. The scene is only rendered when the color
checkbox changes. The change causes the options
object to update which triggers a new AsciiEffect
to be created. The effect is passed into the <Scene>
component and an $effect
is used to rerender the scene.