EN
JavaScript - how to draw pixel on canvas element?
7
points
In JavaScript, it is not possible to get direct access to a single pixel on the canvas element, but there are some tricks on how to do it. In this article, simple approaches have been presented.
Quick solution:
function drawPixel(context, x, y, color) {
var roundedX = Math.floor(x);
var roundedY = Math.floor(y);
context.fillStyle = color || '#000';
context.fillRect(roundedX, roundedY, 1, 1);
}
// Usage example:
var canvas = document.querySelector('#my-canvas');
var context = canvas.getContext('2d');
drawPixel(context, 20, 10, 'red'); // x=20 y=10
Presented solutions in the article:
- drawing 1x1 px rectangle on canvas with
fillRect()
, - drawing 1x1 px image on canvas with
putImageData()
, - drawing multiple pixels on created image data
(~double buffering withcreateImageData()
andputImageData()
).
Performance test (on Ryzen 9 5900x and GeForce GTX 970):
Hint: running the below examples pay attention on the time necessary to draw pixels.
1. Drawing single pixel with fillRect()
method example
This approach is useful when we want to draw single pixels on existing drawing.
// ONLINE-RUNNER:browser;
<!doctype html>
<html>
<head>
<style>
#my-canvas { border: 1px solid gray; }
</style>
</head>
<body>
<canvas id="my-canvas" width="200" height="200"></canvas>
<script>
function drawPixel(context, x, y, color) {
// Math.floor() method is used to decrease smoothing when numbers have decimal parts.
var roundedX = Math.floor(x);
var roundedY = Math.floor(y);
context.fillStyle = color || '#000';
context.fillRect(roundedX, roundedY, 1, 1);
}
// Usage example:
var colors = [
'#ff0000', // red
'#00ff00', // green
'#0000ff' // blue
];
var canvas = document.querySelector('#my-canvas');
var context = canvas.getContext('2d');
var t1 = new Date();
for(var i = 0; i < 10000; ++i) {
var x = canvas.width * Math.random();
var y = canvas.height * Math.random();
var color = colors[i % colors.length];
drawPixel(context, x, y, color);
}
var t2 = new Date();
var dt = t2 - t1;
console.log('elapsed time = ' + dt + ' ms');
</script>
</body>
</html>
2. putImageData
method with single-pixel class example
This approach is useful when we want to draw single pixels on existing drawing.
Note: this solution has weak perfomrance.
// ONLINE-RUNNER:browser;
<!doctype html>
<html>
<head>
<style>
#my-canvas { border: 1px solid gray; }
</style>
</head>
<body>
<canvas id="my-canvas" width="200" height="200"></canvas>
<script>
function Drawer(context) {
// To increase performance, createImageData() method should be executed only once.
var image = context.createImageData(1, 1); // 1x1 px image used to draw pixel
this.drawPixel = function(x, y, color) {
var data = image.data;
data[0] = color.r;
data[1] = color.g;
data[2] = color.b;
data[3] = color.a;
context.putImageData(image, x, y);
};
}
// Usage example:
var colors = [
{r: 255, g: 0, b: 0, a: 255}, // red
{r: 0, g: 255, b: 0, a: 255}, // green
{r: 0, g: 0, b: 255, a: 255} // blue
];
var canvas = document.querySelector('#my-canvas');
var context = canvas.getContext('2d');
var drawer = new Drawer(context);
var t1 = new Date();
for(var i = 0; i < 10000; ++i) {
var x = canvas.width * Math.random();
var y = canvas.height * Math.random();
var color = colors[i % colors.length];
drawer.drawPixel(x, y, color);
}
var t2 = new Date();
var dt = t2 - t1;
console.log('elapsed time = ' + dt + ' ms');
</script>
</body>
</html>
3. putImageData
method with ~double buffering example
This approach is useful when we want to draw everything by own.
Note: this solution has very good perfomrance.
// ONLINE-RUNNER:browser;
<!doctype html>
<html>
<head>
<style>
#my-canvas { border: 1px solid gray; }
</style>
</head>
<body>
<canvas id="my-canvas" width="200" height="200"></canvas>
<script>
function Drawer(context) {
var canvas = context.canvas;
// To increase performance, createImageData() method should be executed only once.
var image = context.createImageData(canvas.width, canvas.height);
this.drawPixel = function(x, y, color) {
var data = image.data;
// Math.floor() method is used to decrease smoothing when numbers have decimal parts.
var roundedX = Math.floor(x);
var roundedY = Math.floor(y);
var index = 4 * (canvas.width * roundedY + roundedX);
data[index + 0] = color.r;
data[index + 1] = color.g;
data[index + 2] = color.b;
data[index + 3] = color.a;
};
this.swapBuffer = function() {
context.putImageData(image, 0, 0);
};
}
// Usage example:
var colors = [
{r: 255, g: 0, b: 0, a: 255}, // red
{r: 0, g: 255, b: 0, a: 255}, // green
{r: 0, g: 0, b: 255, a: 255} // blue
];
var canvas = document.querySelector('#my-canvas');
var context = canvas.getContext('2d');
var drawer = new Drawer(context);
var t1 = new Date();
for(var i = 0; i < 10000; ++i) {
var x = canvas.width * Math.random();
var y = canvas.height * Math.random();
var color = colors[i % colors.length];
drawer.drawPixel(x, y, color);
}
drawer.swapBuffer();
var t2 = new Date();
var dt = t2 - t1;
console.log('elapsed time = ' + dt + ' ms');
</script>
</body>
</html>