How to Repeat an Animated Texture in a Flat Plane

I was recently attempting to get an animated texture to repeat without causing the texture to repeat everywhere else it appeared.

Specifically, I have a routine that creates animated 512x512 Displacement and Normal Maps. I had a 512x512 Plane that displayed the Maps perfectly. I wanted to create a larger Plane that also displayed the Map at the same 512x512 size. For simplicity, assume that the larger Plane is 2x larger (1024x1024). This means that the texture must be repeated 4x on the larger Plane.

With a static texture, you can use texture.repeat.set(2,2) and, as this example shows, it will repeat the textures perfectly.

However, when I tried the same approach with my animated map, it caused the original texture to also repeat - as shown here.

One approach would have been to divide the larger Plane into segments (a 2x2 grid) and then load the texture in each face. Because of recent changes, there is not a lot of guidance as to how to do that. Furthermore, I might run into problem if I wanted to use a displacement map in the larger grid since the texture would have to cover several segments.

The solution I discovered was to do the opposite. Instead, of subdividing the larger Plane, I could construct the larger Plane from smaller planes and then combine them with the mergeGeometries routine from the BufferGeometryUtils module. As you can see here, that did the trick.

And, it also works with an animated displacement map, as shown here.

I have generalized the required steps, as follows:

// Create the Separate Grids
let grids = [];		// Temporary storage
let siz = 512;		// Size of the smaller Plane
let stp = 2;		// Create a 2x2 grid
let idx = 0;		// index
let ctr = (0.5*stp-0.5)*siz;	// 2 = 0.5; 3 = 1.0; 4 = 1.5
for (let z = 0; z < stp; z++) {
	for (let x = 0; x < stp; x++) {
		grids[idx] = new THREE.PlaneGeometry(siz,siz);
		grids[idx].rotateX(-Math.PI * 0.5);
		grids[idx].translate(x*siz-ctr, 0, z*siz-ctr);
// Combine the Grids
let max = stp*stp;
for (let i = 1; i < max; i++) {
	grids[0] = BufferGeometryUtils.mergeGeometries([grids[0], grids[i]], false);
geometry = grids[0];

Using “ctr”, as calculated above, should insure that the combined Plane is perfectly centered.

I would, of course, appreciate hearing if there is a different and/or better way to do this.