You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
nebulaColor1 = '#8a2be2',
nebulaColor2 = '#ff1493',
starDensity = 0.0015,
blendMode = 'screen',
spaceOpacity = 1.0
) {
// Determine canvas dimensions from the original image
const width = originalImg.width;
const height = originalImg.height;
// Create the main canvas and context
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Draw the original image as the base
ctx.drawImage(originalImg, 0, 0, width, height);
// Create a temporary canvas to generate the space/cosmos effect
const spaceCanvas = document.createElement('canvas');
spaceCanvas.width = width;
spaceCanvas.height = height;
const spaceCtx = spaceCanvas.getContext('2d');
// Helper function to extract RGB values from a color string (supports hex, rgb, named colors)
function getRgb(colorStr) {
const tempCtx = document.createElement('canvas').getContext('2d');
tempCtx.fillStyle = colorStr;
tempCtx.fillRect(0, 0, 1, 1);
const data = tempCtx.getImageData(0, 0, 1, 1).data;
return `${data[0]}, ${data[1]}, ${data[2]}`;
}
// Fill the space canvas with a very dark blue/black background
spaceCtx.fillStyle = '#020208';
spaceCtx.fillRect(0, 0, width, height);
// Add Nebula Clouds using radial gradients
const colors = [nebulaColor1, nebulaColor2];
spaceCtx.globalCompositeOperation = 'lighter';
colors.forEach((colorStr) => {
const rgb = getRgb(colorStr);
// Create 3 nebula clusters per color
for (let i = 0; i < 3; i++) {
const cx = Math.random() * width;
const cy = Math.random() * height;
// Clouds cover a good chunk of the image
const radius = Math.max(width, height) * (0.3 + Math.random() * 0.5);
const gradient = spaceCtx.createRadialGradient(cx, cy, 0, cx, cy, radius);
gradient.addColorStop(0, `rgba(${rgb}, 0.5)`);
gradient.addColorStop(0.4, `rgba(${rgb}, 0.2)`);
gradient.addColorStop(1, `rgba(${rgb}, 0)`);
spaceCtx.fillStyle = gradient;
spaceCtx.beginPath();
spaceCtx.arc(cx, cy, radius, 0, Math.PI * 2);
spaceCtx.fill();
}
});
// Add Stars
const starCount = Math.floor(width * height * starDensity);
// Normal distant stars
for (let i = 0; i < starCount; i++) {
const x = Math.random() * width;
const y = Math.random() * height;
const r = Math.random() * 1.5;
const opacity = Math.random() * 0.8 + 0.2; // 0.2 to 1.0
spaceCtx.beginPath();
spaceCtx.arc(x, y, r, 0, Math.PI * 2);
spaceCtx.fillStyle = `rgba(255, 255, 255, ${opacity})`;
spaceCtx.fill();
}
// Larger, glowing stars (Fewer in count)
const bigStarCount = Math.floor(starCount / 40);
for (let i = 0; i < bigStarCount; i++) {
const x = Math.random() * width;
const y = Math.random() * height;
const r = Math.random() * 2 + 1;
const gradient = spaceCtx.createRadialGradient(x, y, 0, x, y, r * 2);
gradient.addColorStop(0, 'rgba(255, 255, 255, 1)');
gradient.addColorStop(0.3, 'rgba(200, 220, 255, 0.8)');
gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
spaceCtx.beginPath();
spaceCtx.arc(x, y, r * 2, 0, Math.PI * 2);
spaceCtx.fillStyle = gradient;
spaceCtx.fill();
// Add an optional cross-flare to some of the localized bright stars
if (Math.random() > 0.5) {
const crossSize = r * 6;
spaceCtx.fillStyle = 'rgba(255, 255, 255, 0.4)';
spaceCtx.fillRect(x - crossSize / 2, y - 0.5, crossSize, 1); // Horizontal
spaceCtx.fillRect(x - 0.5, y - crossSize / 2, 1, crossSize); // Vertical
}
}
// Blend the Space cosmos effect over the original image
// Blend modes like 'screen' or 'lighter' work exceptionally well as they
// drop the darks of the cosmos and seamlessly combine the nebulae & stars with light.
ctx.globalAlpha = Math.max(0, Math.min(1, spaceOpacity)); // Clamp opacity between 0 & 1
ctx.globalCompositeOperation = blendMode;
ctx.drawImage(spaceCanvas, 0, 0);
// Reset properties for standard canvas state continuation context
ctx.globalAlpha = 1.0;
ctx.globalCompositeOperation = 'source-over';
return canvas;
}
Apply Changes