I am creating the grouts around the image texture before repeating it. in this way if color or thickness needs to changed of grout then also I have to create textures again and repeat them.
now trying to find a way by which create a separate layer / mesh just before the actual wall mesh to avoid creating textures every single tile when updating the grout properties / params.
only grout properties should be updated and not texture needs to be as it is.
grouts are the lines around the actual image texture.
any suggestion or guidance helps
Thanks 
codepen : https://codepen.io/Vatsal-Pandya-the-solid/pen/PovEqgp
texture function :
function updateTexture({ element, rotateAngle = 90 }) {
const tileData = tilesData[Math.floor(Math.random() * tilesData.length)];
let imageUrls = tileData.imgUrl;
// Array to hold loaded textures
let textures = [];
// Load images asynchronously
Promise.all(imageUrls.map(loadImage))
.then((images) => {
// Process each loaded image
images.forEach((image) => {
// Create a canvas and its context
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
// Draw the image on the canvas
canvas.width = nextPowerOf2(image.width);
canvas.height = nextPowerOf2(image.height);
context.drawImage(image, 0, 0, canvas.width, canvas.height);
// Draw grout lines if they are enabled
if (grout_v_width > 0 || grout_h_width > 0) {
context.strokeStyle = grout_color; // Use updated grout color
context.globalAlpha = grout_alpha;
// Draw vertical grout lines
if (grout_v_width > 0) {
context.lineWidth = grout_v_width;
context.beginPath();
context.moveTo(0, 0);
context.lineTo(canvas.width, 0);
context.closePath();
context.stroke();
context.beginPath();
context.moveTo(0, canvas.height);
context.lineTo(canvas.width, canvas.height);
context.closePath();
context.stroke();
}
// Draw horizontal grout lines
if (grout_h_width > 0) {
context.lineWidth = grout_h_width;
context.beginPath();
context.moveTo(0, 0);
context.lineTo(0, canvas.height);
context.closePath();
context.stroke();
context.beginPath();
context.moveTo(canvas.width, 0);
context.lineTo(canvas.width, canvas.height);
context.closePath();
context.stroke();
}
context.globalAlpha = 1;
}
const texture = new THREE.Texture(canvas);
texture.premultiplyAlpha = false;
texture.needsUpdate = true;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
texture.repeat.set(5, 10);
textures.push(texture);
});
// Apply textures randomly to the element's material
const randomTextureIndex = Math.floor(Math.random() * textures.length);
element.material.map = textures[randomTextureIndex];
element.material.needsUpdate = true; // Important to update the material
renderer.render(scene, camera);
})
.catch((error) => {
console.error("Error loading images:", error);
});
// Function to load an image
function loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = "anonymous"; // Ensure cross-origin loading
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
}