Many shapes
Populate the canvas with hundreds of shapes to see tldraw's performance optimizations in action.
import {
Editor,
TLCreateShapePartial,
Tldraw,
TldrawUiButton,
createShapeId,
useEditor,
} from 'tldraw'
import 'tldraw/tldraw.css'
// [1]
const GEO_TYPES = [
'rectangle',
'ellipse',
'triangle',
'diamond',
'pentagon',
'hexagon',
'octagon',
'star',
'cloud',
'heart',
] as const
const COLORS = [
'black',
'grey',
'light-violet',
'violet',
'blue',
'light-blue',
'yellow',
'orange',
'green',
'light-green',
'light-red',
'red',
] as const
const FILLS = ['none', 'semi', 'solid', 'pattern'] as const
const DASHES = ['draw', 'solid', 'dashed', 'dotted'] as const
const SIZES = ['s', 'm', 'l'] as const
function pick<T>(arr: readonly T[]): T {
return arr[Math.floor(Math.random() * arr.length)]
}
// [2]
function generateShapes(editor: Editor, count: number) {
const cols = Math.ceil(Math.sqrt(count * 1.5))
const cellW = 200
const cellH = 200
const padding = 20
const shapes: TLCreateShapePartial[] = []
for (let i = 0; i < count; i++) {
const col = i % cols
const row = Math.floor(i / cols)
const x = col * cellW + padding
const y = row * cellH + padding
const w = cellW - padding * 2
const h = cellH - padding * 2
// Mix shape types: mostly geo, some notes
if (i % 7 === 0) {
// Every 7th shape is a sticky note
shapes.push({
id: createShapeId(),
type: 'note' as const,
x: x + Math.random() * 20,
y: y + Math.random() * 20,
props: {
color: pick(COLORS),
size: pick(SIZES),
},
})
} else {
shapes.push({
id: createShapeId(),
type: 'geo' as const,
x: x + Math.random() * 20,
y: y + Math.random() * 20,
props: {
geo: pick(GEO_TYPES),
w: w * (0.6 + Math.random() * 0.4),
h: h * (0.6 + Math.random() * 0.4),
color: pick(COLORS),
fill: pick(FILLS),
dash: pick(DASHES),
size: pick(SIZES),
},
})
}
}
editor.run(() => {
editor.createShapes(shapes)
})
}
// [3]
function Controls() {
const editor = useEditor()
const handleGenerate = (count: number) => {
generateShapes(editor, count)
editor.zoomToFit({ animation: { duration: 300 } })
}
const handleClear = () => {
editor.run(() => {
const ids = [...editor.getCurrentPageShapeIds()]
if (ids.length > 0) {
editor.deleteShapes(ids)
}
})
}
return (
<div style={{ display: 'flex', gap: 4, padding: 8, flexWrap: 'wrap' }}>
<TldrawUiButton type="normal" onClick={() => handleGenerate(200)}>
Add 200 shapes
</TldrawUiButton>
<TldrawUiButton type="normal" onClick={() => handleGenerate(500)}>
Add 500 shapes
</TldrawUiButton>
<TldrawUiButton type="normal" onClick={() => handleGenerate(1000)}>
Add 1000 shapes
</TldrawUiButton>
<TldrawUiButton type="normal" onClick={handleClear}>
Clear all
</TldrawUiButton>
</div>
)
}
export default function ManyShapesExample() {
return (
<div className="tldraw__editor">
<Tldraw
onMount={(editor) => {
// Start with 500 shapes
generateShapes(editor, 500)
editor.zoomToFit({ animation: { duration: 0 } })
}}
components={{ TopPanel: Controls }}
/>
</div>
)
}
/*
[1]
Arrays of shape properties used to generate varied shapes. The mix of geo
types, fills, dashes, and colors creates a visually diverse canvas that
exercises many different rendering paths.
[2]
Shapes are laid out in a grid with slight random offsets for visual variety.
Every 7th shape is a sticky note, which has box shadow LOD behavior—zoom out
far enough and the shadows disappear. Geo shapes with "draw" dash style will
also simplify to solid strokes at low zoom levels, and "pattern" fills will
flatten to solid colors.
[3]
The control panel lets you add shapes in batches or clear the canvas. All
shape creation is wrapped in editor.run() so observers receive a single
batched update, which is one of the performance techniques described in the
performance docs.
*/
This example creates a large number of shapes to demonstrate how tldraw handles dense canvases. Try zooming out to see level-of-detail transitions: sticky note shadows disappear, draw-style strokes simplify to solid paths, and pattern fills flatten to solid colors.
Use "Zoom to fit" to see all shapes at once and observe how culling, debounced zoom, and LOD work together.
Is this page helpful?
Prev
Mark examsNext
Snowstorm