Ver ejemplo en vivo Descargar código fuente
Continuando con nuestros experimentos visuales utilizando HTML5, Canvas y JavaScript, hoy vamos a explorar cómo generar efectos de iluminación desde cero. En este artículo, aprenderás a crear un punto luminoso manipulando directamente los píxeles del lienzo, una técnica fundamental para entender cómo funcionan los motores gráficos y el procesamiento de imágenes.
¿Cómo crear un punto luminoso con JavaScript y Canvas?
Aunque existen métodos automáticos en Canvas para crear sombras, el enfoque de este experimento es matemático. Vamos a calcular la iluminación de cada píxel basándonos en su distancia respecto a un punto central.
Aquí tienes el código modernizado utilizando estándares de ES6+:
const canvas = document.getElementById('canv');
const ctx = canvas.getContext('2d');
const width = 800;
const height = 400;
// Definimos nuestro "sol" o punto de luz
const lightSource = {
x: 100,
y: 100,
intensity: 15
};
const drawLightPoint = () => {
// Obtenemos el espacio para los píxeles
const imageData = ctx.createImageData(width, height);
const pixels = imageData.data;
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
// Calculamos la distancia entre el píxel actual y la fuente de luz
const d = Math.sqrt(Math.pow(x - lightSource.x, 2) + Math.pow(y - lightSource.y, 2));
// Calculamos la densidad de luz (a menor distancia, mayor intensidad)
const density = lightSource.intensity / d;
// Índice en el arreglo unidimensional (RGBA)
const index = (x + y * width) * 4;
// Manipulamos los canales de color
pixels[index] = density * 70; // Rojo
pixels[index + 1] = density * 70; // Verde
pixels[index + 2] = density * 38; // Azul (70 * 0.55)
pixels[index + 3] = 255; // Alpha (Opaco)
}
}
// Volcamos los datos al canvas
ctx.putImageData(imageData, 0, 0);
};
drawLightPoint();Análisis del código y la lógica matemática
Para que este efecto funcione, el secreto reside en la fórmula de la distancia entre dos puntos.
La importancia de la distancia
En la línea const density = lightSource.intensity / d; ocurre la magia. Al dividir la intensidad por la distancia (d), logramos que los píxeles más cercanos al centro tengan valores de color muy altos (blancos/amarillos), mientras que los lejanos tienden a cero (negro).

El arreglo de píxeles (ImageData)
Un error común al empezar con Canvas es pensar que los píxeles se manejan como una matriz bidimensional. En realidad, getImageData devuelve un TypedArray unidimensional donde cada píxel ocupa 4 posiciones consecutivas (R, G, B y A).
Si nuestro lienzo es de 800x400, el total de datos es: 800 (ancho) * 400 (alto) * 4 (canales) = 1,280,000 elementos.
Variando el color del punto luminoso
Las multiplicaciones por valores constantes sirven para variar el color, pruebas distintos valores y verás cambios de colores.
Calculando la posición a pintar
El cálculo de la variable idx es para recorrer un arreglo de una dimensión a partir de los contadores (x yy) de una matriz, esto se debe a que la función getImageData devuelve un array de una dimensión y no una matriz como podríamos esperar.
Se multiplica por cuatro debido a que hay que agregar los canales RGBA los cuales son 4 y se suman al total de la matriz; si ejecutas el siguiente comando:
id.data.lengthVeras que retorna -para nuestro experimento- 1280000; es decir la longitud de la data es calculada como:
AC * LC * TRGBAEn donde:
- AC = Ancho del Canvas.
- LC = Largo del Canvas.
- TRGBA = Tamaño del canal RGBA (4).
Que en nuestro experimento viene siendo:
800 * 400 * 4 = 1280000Pitando en el Canvas el punto luminoso
Finalmente pintamos el color calculado a través de la distancia entre dos puntos para cada canal RGBA:
pxl[idx] = dens * 70;
pxl[idx + 1] = (dens * 70);
pxl[idx + 2] = (dens * 70) * 0.55;
pxl[idx + 3] = 255;Optimización: El método de Gradientes Radiales
El método anterior es excelente para aprender, pero procesar un millón de operaciones en el CPU cada vez que quieras mover la luz es ineficiente. Para aplicaciones en tiempo real o juegos, la API de Canvas ofrece createRadialGradient, que utiliza la aceleración por hardware (GPU).
// Alternativa eficiente para producción
const gradient = ctx.createRadialGradient(100, 100, 0, 100, 100, 50);
gradient.addColorStop(0, 'rgba(255, 255, 200, 1)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);Conclusión y siguientes pasos
Entender cómo manipular píxeles individuales te abre las puertas a crear filtros de imagen, efectos de partículas y sombreadores (shaders) personalizados en el navegador. Aunque para efectos simples los gradientes son mejores, la manipulación directa de ImageData es la base de la computación gráfica avanzada.
Si te ha gustado este experimento, no te pierdas nuestro próximo artículo sobre cómo crear una onda sinusoidal con Canvas y JavaScript, donde aplicaremos funciones trigonométricas para dar movimiento a nuestros gráficos.