Transform any image into beautiful low-poly triangular art with a single line of code. No dependencies, no build steps — just add one <script>
tag and you're ready to go.
Interactive image triangulator — Drag & drop images, adjust settings in real-time, and export as PNG or SVG.
- 🖼️ Hosted Web App — Interactive UI with live preview and export
- ⚡ Simple API — One function call:
ImageToTriangle.triangulate()
- 🎨 Advanced Controls — Brightness, contrast, edge detection, point density, and more
- 📐 Resolution Separation — Analyze at low resolution, render at any size
- 🎲 Deterministic — Same seed = same result every time
- 🔄 Async & Progressive — Built-in progress callbacks for smooth UX
- 📦 Zero Dependencies — Pure JavaScript, works everywhere
<script src="https://cdn.jsdelivr.net/gh/Tezumie/Image-to-triangle/src/image-to-triangle.js"></script>
// Works with <img> or <canvas>
const result = await ImageToTriangle.triangulate({
image: yourImageOrCanvas,
points: 2000,
format: 'canvas' // or 'image' or 'svg'
});
document.body.appendChild(result);
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/gh/Tezumie/Image-to-triangle/src/image-to-triangle.js"></script>
</head>
<body>
<img id="source" src="your-image.jpg" crossorigin="anonymous">
<div id="output"></div>
<script>
document.getElementById('source').onload = async () => {
const result = await ImageToTriangle.triangulate({
image: document.getElementById('source'),
resolution: 800, // Analysis size (keeps layout consistent)
outputResolution: 1600, // Render size (for crisp output)
points: 3000,
preprocess: {
brightness: 1.2, // Brighten the image
contrast: 1.1, // Boost contrast
edgeBoost: 0.6 // Enhance edge detection
},
settings: {
darkStrength: 4.0, // Bias toward darker areas
showWires: true,
wireColor: '#ffffff',
seed: 42 // Reproducible results
},
onProgress: (percent) => console.log(`${percent}% complete`)
});
document.getElementById('output').appendChild(result);
};
</script>
</body>
</html>
let triangulated;
async function setup() {
createCanvas(800, 600);
// Your drawing code here...
background(50);
for (let i = 0; i < 100; i++) {
fill(random(255), random(255), random(255));
circle(random(width), random(height), random(20, 80));
}
// Triangulate the canvas (dimension-agnostic approach)
triangulated = await ImageToTriangle.triangulate({
image: canvas,
resolution: 1200, // Fixed analysis size
outputResolution: width, // Match current canvas size
points: 2500,
settings: {
minDist: 1200 / 70, // Scale settings to analysis resolution
edgeSamples: 1200 / 10,
wireWidth: 1200 / 500
},
format: 'canvas'
});
redraw();
}
function draw() {
if (triangulated) {
// Convert to p5.Image and display
let img = createImage(triangulated.width, triangulated.height);
img.drawingContext.drawImage(triangulated, 0, 0);
image(img, 0, 0);
}
}
const result = await ImageToTriangle.triangulate({
// Required
image: HTMLCanvasElement | HTMLImageElement,
// Resolution control (key feature!)
resolution: 800, // Analysis width - triangulation happens here
outputResolution: 1600, // Final render width - scales triangles up/down
// Image preprocessing
preprocess: {
brightness: 1.0, // 0.1 - 3.0
contrast: 1.0, // 0.1 - 3.0
saturation: 1.0, // 0.0 - 2.0
gamma: 1.0, // 0.1 - 3.0
blur: 0, // 0 - 10 pixels
invert: false,
densityMode: 'luma', // 'luma' | 'lumaBoost' | 'red' | 'green' | 'blue'
edgeBoost: 0.5 // 0.0 - 2.0 (enhances edge detection)
},
// Triangulation settings
settings: {
points: 2000, // Number of points to place
darkStrength: 4.0, // Bias toward dark areas (0.1 - 8.0)
minDist: 8, // Minimum point spacing
edgeSamples: 20, // Border enhancement points
showWires: true, // Show triangle edges
wireColor: '#ffffff', // Edge color (hex)
wireWidth: 1.0, // Edge thickness
seed: 1, // Random seed for reproducible results
settingsSpace: 'analysis' // 'analysis' | 'output' - which space to interpret sizes
},
// Output options
format: 'canvas', // 'canvas' | 'image' | 'svg'
onProgress: (percent) => console.log(`${percent}% complete`)
});
resolution
— Controls where triangulation happens. Keep constant for consistent layouts across different output sizesoutputResolution
— Controls final render size. Scale this up for high-quality exports without changing triangle placementsettingsSpace
— Whether size params (minDist
,wireWidth
, etc.) are in analysis or output unitsdarkStrength
— Higher values (2-6) place more points in darker image areas for better detailedgeBoost
— Enhances edge detection using Sobel filters for better triangle placement along boundariesdensityMode
— How to calculate point density:'luma'
(brightness),'lumaBoost'
(enhanced contrast), or individual color channels
🖼️ Web App — Try the hosted app
Interactive image triangulator with drag & drop, real-time preview, and PNG/SVG export
💻 API Demo — Live code editor
Dimension-agnostic p5.js integration showing the JavaScript API in action
Generated using p5.js with random circles and triangulation
For consistent layouts across sizes:
// Keep analysis resolution fixed, vary output size
const layout1 = await triangulate({ resolution: 1200, outputResolution: 800, ... });
const layout2 = await triangulate({ resolution: 1200, outputResolution: 2400, ... });
// Same triangle layout, different render sizes!
For high-quality exports:
const result = await triangulate({
resolution: 800, // Fast analysis
outputResolution: 3200, // 4x render resolution
format: 'svg' // Vector output
});
MIT © Tezumie
This project was created using Codevre — a 100% free, browser-based code editor I built specifically for creative coders.
It’s designed to make experimenting with interactive visuals, generative art, and web-based sketches fast and accessible, without setup or installation.
If you enjoy working with projects like this, give Codevre a try!
- Image-to-Pixel — A companion library for turning images into dithered pixel art.
⭐ Star this repo if it helped you create something awesome!