You can edit the below JavaScript code to customize the image tool.
function processImage(
originalImg,
lightColorStr = "210,180,140", // Light wood color (RGB string, e.g., "R,G,B")
darkColorStr = "139,105,60", // Dark wood color (RGB string, e.g., "R,G,B")
grainDensity = 20, // Number of grain lines/features across image height. Higher means finer grain. (e.g., 5-100)
turbulenceStrength = 0.1, // Amplitude of horizontal waviness for vertical grains. (e.g., 0-0.5)
// Value is relative to normalized coordinates; 0 means straight grains.
turbulenceFrequency = 3, // Number of full waves for turbulence across image width. (e.g., 1-10)
stretchFactor = 3.0, // How much grains are stretched vertically. >= 1. Higher makes grains look longer.
ringEffect = 0.0, // Mix factor for concentric ring pattern (0-1). 0 for mostly vertical grains, 1 for mostly rings.
ringDensity = 15, // Number of rings from center to mid-edge if ringEffect > 0. (e.g., 5-50)
noiseAmount = 0.05 // Amount of fine random noise (0-0.3, relative to color range). Adds realism.
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
// Helper function to parse color strings
const parseColor = (colorStr, defaultColor) => {
try {
const parts = colorStr.split(',').map(s => parseInt(s.trim(), 10));
if (parts.length === 3 && parts.every(p => !isNaN(p) && p >= 0 && p <= 255)) {
return parts;
}
} catch (e) {
// Fehler beim Parsen, Standard verwenden
}
return defaultColor;
};
const lightColor = parseColor(lightColorStr, [210, 180, 140]); // Default Tan
const darkColor = parseColor(darkColorStr, [139, 105, 60]); // Default Peru-like
// Ensure stretchFactor is valid
const currentStretchFactor = Math.max(1.0, stretchFactor);
// Create new image data
const imageData = ctx.createImageData(width, height);
const data = imageData.data;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const idx = (y * width + x) * 4;
// Normalized coordinates (0 to 1)
const u = x / width;
const v = y / height;
// --- Vertical Grain Calculation ---
// Effective y-coordinate for grain lines, stretched and with horizontal turbulence.
// `v * currentStretchFactor`: stretches the y-coordinate.
// `turbulenceStrength * Math.sin(...)`: adds horizontal waviness.
// `u * turbulenceFrequency * Math.PI * 2`: creates `turbulenceFrequency` full sine waves across width.
const y_effective_turbulence = turbulenceStrength * Math.sin(u * turbulenceFrequency * Math.PI * 2);
const y_effective = v * currentStretchFactor + y_effective_turbulence;
// Normalize y_effective back to a 0-1 range relative to its stretched scale for consistent density.
// This makes grainDensity represent features across an equivalent of original height.
const y_normalized_for_density = y_effective / currentStretchFactor;
// Base grain value using sine wave.
// `grainDensity * Math.PI` creates `grainDensity/2` full sine waves (dark+light pairs),
// meaning `grainDensity` features (bands).
let mainGrainVal = (Math.sin(y_normalized_for_density * grainDensity * Math.PI) + 1) / 2;
// --- Ring Effect Calculation ---
if (ringEffect > 0 && ringEffect <= 1) {
const dx_center = u - 0.5; // Centered u coordinate
const dy_center = v - 0.5; // Centered v coordinate
// Adjust aspect ratio of rings. If grains are stretched vertically, rings often appear wider.
// This scaling makes dy component smaller for distance calculation, resulting in wider ellipses.
const ring_dy_scale = 1.0 / (currentStretchFactor * 0.5 + 0.5) ; // Heuristic based on stretch
const dist_from_center = Math.sqrt(dx_center * dx_center + (dy_center * ring_dy_scale) * (dy_center * ring_dy_scale));
// `dist_from_center` is roughly 0 to 0.5 (center to mid-edge for square).
// `ringDensity * Math.PI * 2` means `ringDensity` full cycles if `dist_from_center` went 0 to 1.
// So, for `dist_from_center` up to 0.5, this gives `ringDensity/2` rings from center to edge.
let ringVal = (Math.sin(dist_from_center * ringDensity * Math.PI * 2) + 1) / 2;
// Blend main grain with ring grain
mainGrainVal = mainGrainVal * (1 - ringEffect) + ringVal * ringEffect;
}
mainGrainVal = Math.max(0, Math.min(1, mainGrainVal)); // Clamp to 0-1
// --- Add Fine Noise ---
// `noiseAmount` is relative to the 0-1 color range.
let finalGrainVal = mainGrainVal + (Math.random() - 0.5) * noiseAmount;
finalGrainVal = Math.max(0, Math.min(1, finalGrainVal)); // Clamp again
// --- Determine Final Color ---
// Interpolate between light and dark wood colors based on final grain value
const r = lightColor[0] * (1 - finalGrainVal) + darkColor[0] * finalGrainVal;
const g = lightColor[1] * (1 - finalGrainVal) + darkColor[1] * finalGrainVal;
const b = lightColor[2] * (1 - finalGrainVal) + darkColor[2] * finalGrainVal;
data[idx] = r;
data[idx + 1] = g;
data[idx + 2] = b;
data[idx + 3] = 255; // Opaque
}
}
ctx.putImageData(imageData, 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 Wood Texture Filter is an online tool designed to apply a realistic wood texture effect to images. Users can customize various parameters, including the colors of the wood grains, grain density, turbulence strength, and the presence of concentric rings. This tool is ideal for graphic designers, artists, and enthusiasts looking to enhance their images with a natural wood-like appearance for use in digital artwork, backgrounds, or texture overlays in design projects.