You can edit the below JavaScript code to customize the image tool.
async function processImage(
originalImg,
veinPeriodX = 64, // Defines the approximate width of marble veins horizontally, in pixels. Lower values = denser veins.
veinPeriodY = 128, // Defines the approximate height of marble veins vertically, in pixels. Lower values = denser veins.
turbulenceFactor = 1.5, // Controls the amount of chaotic distortion in the veins. Higher values lead to more swirling. Range: 0 (straight veins) to ~5.
noiseScale = 0.03, // Scale of the underlying noise pattern that creates turbulence. Smaller values = larger, smoother noise features. Range: ~0.001 to ~0.1.
noiseOctaves = 4, // Number of noise layers summed for turbulence. Higher values add more detail but increase computation time. Range: 1 to 8.
noisePersistence = 0.5, // Determines how much each successive octave contributes to the noise detail. Range: ~0.2 to ~0.8.
noiseLacunarity = 2.0, // Controls how much the frequency increases for each successive octave of noise. Range: ~1.5 to ~3.0.
veinColor1Str = "220,220,230", // Primary color of the marble (e.g., the lighter part or body of the marble), as an "R,G,B" string.
veinColor2Str = "40,40,60", // Secondary color of the marble (e.g., the darker part or the veins/fractures), as an "R,G,B" string.
mixOriginalOpacity = 0.0, // Blending factor with the original image. 0.0 means 100% marble texture, 1.0 means 100% original image. Range: 0.0 to 1.0.
randomSeed = 0 // Seed for the random noise generator. Use 0 for a different pattern each time, or any other number for a fixed, reproducible pattern.
) {
// Parse color strings to [R, G, B] arrays. Added trim() for robustness.
// Fallback to 0 for invalid numbers to prevent NaN issues in color calculations.
const color1 = veinColor1Str.split(',').map(s => Number(s.trim()) || 0);
const color2 = veinColor2Str.split(',').map(s => Number(s.trim()) || 0);
const canvas = document.createElement('canvas');
// Optimization hint for frequent readbacks (getImageData)
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
canvas.width = width;
canvas.height = height;
// Draw the original image first if it needs to be mixed or its alpha channel is used
ctx.drawImage(originalImg, 0, 0, width, height);
const originalImageData = ctx.getImageData(0, 0, width, height);
const originalData = originalImageData.data;
const outputImageData = ctx.createImageData(width, height);
const outputData = outputImageData.data;
// Initialize the random seed
const seed = (randomSeed === 0) ? Math.random() * 100000 : Number(randomSeed);
// --- Noise generation functions (Perlin-like value noise) ---
// These are defined inside processImage to keep the solution self-contained.
// Pseudo-random number generator function
const _rand = (ix, iy, currentSeed) => {
// Using fixed large numbers for a simple hash-like behavior.
// ix and iy are expected to be integers.
const RND_A = 134775813;
const RND_C = 1;
const RND_M = 2147483648; // 2^31 for positive results
let val = (ix * 12345 + iy * 67890 + currentSeed * 101112);
// Ensure val is positive before modulo, and handle large numbers if intermediate is negative
val = ((val % RND_M) + RND_M) % RND_M;
val = (val * RND_A + RND_C) % RND_M;
return (val / RND_M); // Result in [0, 1)
};
// Linear interpolation
const _lerp = (a, b, t) => a + t * (b - a);
// Fade function (Perlin's improved version: 6t^5 - 15t^4 + 10t^3) to smooth interpolation
const _fade = (t) => t * t * t * (t * (t * 6 - 15) + 10);
// Coherent noise function (generates a single "octave" of noise)
const _smoothNoise = (x, y, currentSeed) => {
const ix = Math.floor(x); // Integer part of x
const iy = Math.floor(y); // Integer part of y
const fx = x - ix; // Fractional part of x
const fy = y - iy; // Fractional part of y
// Get random values at the corners of the unit cell
const r00 = _rand(ix, iy, currentSeed);
const r10 = _rand(ix + 1, iy, currentSeed);
const r01 = _rand(ix, iy + 1, currentSeed);
const r11 = _rand(ix + 1, iy + 1, currentSeed);
// Apply fade function to fractional parts for smoother interpolation
const u = _fade(fx);
const v = _fade(fy);
// Interpolate between the four corner values
return _lerp(_lerp(r00, r10, u), _lerp(r01, r11, u), v);
};
// Fractal Brownian Motion (FBM) - sums multiple octaves of noise
const _fbm = (x, y, currentSeed, octaves, persistence, lacunarity) => {
let total = 0;
let frequency = 1.0; // Initial frequency for the first octave
let amplitude = 1.0; // Initial amplitude for the first octave
let maxValue = 0; // Used to normalize the result to an approximate [0,1] range
for (let i = 0; i < octaves; i++) {
// Add noise from this octave, using a different seed component (currentSeed + i)
total += _smoothNoise(x * frequency, y * frequency, currentSeed + i) * amplitude;
maxValue += amplitude; // Accumulate max possible amplitude for normalization
amplitude *= persistence; // Reduce amplitude for next octave
frequency *= lacunarity; // Increase frequency for next octave
}
// Avoid division by zero if octaves = 0 or persistence makes amplitude sum to 0
if (maxValue === 0) return 0;
return total / maxValue; // Normalize
};
// --- End of noise functions ---
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
// 1. Calculate turbulence value using FBM
// noiseScale adjusts the "zoom" level of the turbulence pattern
const turbulenceValue = _fbm(x * noiseScale, y * noiseScale, seed, noiseOctaves, noisePersistence, noiseLacunarity);
// 2. Create the marble vein pattern using sine waves distorted by turbulence
// Map turbulenceValue from [0,1] to [-1,1] to control phase shift direction
const turbulencePhaseShift = (turbulenceValue - 0.5) * 2.0;
// Argument for the sine function. Veins are formed by sine waves.
// Turbulence distorts these waves.
const sineWaveArgument =
(x * 2 * Math.PI / Math.max(1, veinPeriodX)) + // Horizontal wave component
(y * 2 * Math.PI / Math.max(1, veinPeriodY)) + // Vertical wave component
turbulenceFactor * Math.PI * turbulencePhaseShift; // Turbulence-induced phase shift
const sineValue = Math.sin(sineWaveArgument);
// Map sineValue from [-1, 1] to [0, 1] to use as an interpolation factor between colors
const marblePatternFactor = (sineValue + 1) / 2;
const idx = (y * width + x) * 4; // Pixel index in ImageData array
// 3. Determine pixel color by interpolating between two vein colors
const marbleR = color1[0] * marblePatternFactor + color2[0] * (1 - marblePatternFactor);
const marbleG = color1[1] * marblePatternFactor + color2[1] * (1 - marblePatternFactor);
const marbleB = color1[2] * marblePatternFactor + color2[2] * (1 - marblePatternFactor);
// 4. Blend with original image if mixOriginalOpacity is greater than 0
if (mixOriginalOpacity > 0 && mixOriginalOpacity <= 1) {
const originalR = originalData[idx];
const originalG = originalData[idx + 1];
const originalB = originalData[idx + 2];
outputData[idx] = marbleR * (1 - mixOriginalOpacity) + originalR * mixOriginalOpacity;
outputData[idx + 1] = marbleG * (1 - mixOriginalOpacity) + originalG * mixOriginalOpacity;
outputData[idx + 2] = marbleB * (1 - mixOriginalOpacity) + originalB * mixOriginalOpacity;
} else { // If mixOriginalOpacity is 0 or invalid, use 100% marble color
outputData[idx] = marbleR;
outputData[idx + 1] = marbleG;
outputData[idx + 2] = marbleB;
}
// Preserve the original alpha channel
outputData[idx + 3] = originalData[idx + 3];
}
}
ctx.putImageData(outputImageData, 0, 0);
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Marble Texture Filter allows users to transform images into a marble-like texture by applying customizable parameters for vein patterns and colors. This tool can be useful for graphic designers, artists, and architects looking to create unique backgrounds, enhance images with artistic effects, or visualize materials in product designs. Users can adjust the density and characteristics of the marble veins, mix the textured output with the original image, and choose specific colors to achieve their desired aesthetic.