
As I was looking for inspiration on Awwwards, I came across this beautiful little site: Bien JouƩ. The site features an infinite image gallery in a 3D space, with some amazing WebGL effects on user interactions.
The infinite gallery fascinated me, and I wondered if I could re-implement the gallery-part with react-three-fiber. After some experiment & fumbling around, I was able to put out a small ādemo-ableā app:
It was a fun & challenging project, and I want to share my approach with you in this small memo. Iāll write about what I think are the two core problems weād need to solve to make an infinite gallery possible:
- How to build an infinite gallery
- How to handle mouse events to move around & create some WebGL effects
Head-ups
Before jumping in the main points, you should know that Iāll only be discussing the above-mentioned problems on a āconceptā level. Iāll not go into any actual technical implementation, nor do I think I should.
I use react-three-fiber for the re-implementation, but with the concepts worked out, I believe you can also create similar solutions with other libraries & languages.
Understanding the core logic & effects
I suggest you take a look at my demo app first to have better visualization of the 2 problems I have mentioned above. Again, they are:
Build an infinite gallery:
- The gallery space is indefinite (no boundary) and user can navigate around with mouse interactions
- No matter which direction they go (vertical, horizontal, diagonal), there will always be images to display
Handle mouse events:
- User can click & drag to move around
- On mouse-down, there will be some distortion effect on the images, depending on their distance to the center of the screen

Building the infinite gallery
Letās say we have an original image grid. Building this grid is simple & totally up to your preferences, so weāll skip this step. For example, in my app, I use a 6 x 5 image grid, with a little offset among the columns to create a masonry-style one.
Idea
Basically, we want a gallery space that expands indefinitely.
The most brute solution I could think of is to duplicate & render more images when needed, but that would also bring up horrendous performance issues, and a session probably wouldnāt last very long before crashing.
Such solution is clearly not viable. Therefore, I try to use a technique that is pretty common in infinite sliders:
- Duplicating the original slides & put them before/after the original ones
- Re-calculating all imagesā position on slide change, to create an āendlessā feel
Performance-wised, itās fantastic. The question now is how to adapt it to fit the problem on hand.
Solution
After putting in some thoughts, I decided to go for the below approach:
- Generate the image grid & save every imageās position. Weāll not be updating their position because that would be really heavy, but they will be needed for future WebGL calculations.
- In stead of tracking every single imageās position, Iāll track the position of them all as a group. This is obviously better for performance, as well as keeping track of the whole gridās position is clearly cleaner & easier than tracking every single image.
- Weāll be duplicating the whole image grid. As a result, I ended up with 3x3 = 9 grids in total, vertically and horizontally, with the original grid in the center:

We will also keep track of all 9 gridsā order: which is the center grid, which are the clones (the boundary grids), and their respective positions. This is important.
- On the other hand, we will also keep track of the userās current ālook-atā position (think of this like a camera), which I see as the center point - the center of the screen. While user is navigating, in a way we can also say that the user is moving the center point around. By default, the user will be looking at the center grid.
- Now upon user navigation, weāll be calculating if the center point is close to the boundary grids, and updating the whole boundary (each grid in the column/row) when needed. For example, if the user is moving past the right boundary, we will update the left columnās position to be after the right bound:

After the position update, we will also update the 9 gridsā order: re-calculating again which one is now the center and which ones belong to the bounds.
- Keep tracking the center point, rinse & repeat!
Using the above-mentioned logic, we can make sure that the user is always looking at the center grid, and the boundary grids that surround it will always be updated to follow the userās ālook-atā position. This will create a feeling that the gallery is infinite, while in fact, there are only 9 grids moving around.
Handle mouse events (create WebGL effects)
This one issue is, fortunately, a tad easier to solve than the first. My approach is:
- Use a global mousemove event listener to calculate the movement distance on user navigation, and update the position of the center point accordingly.
- Upon center point position update, also run the flow to update the gridsā position accordingly
- Create some WebGL effects depending on the center point position & the distance between each image to the center point. Remember I said that each imageās position is needed for future WebGL calculations? Well here it is.
You might be wondering how to calculate the distance because I said that weād not be updating the imagesā position. But in fact, we actually do! Because we are keeping track of the grids, while:
- The imagesā position are relative to the grid that contain them
- The gridās position are relative to the global position
Having both the image and the gridās position, we can calculate the imageās exact global position. Now calculating the distance between them & the center point is a breeze.
The WebGL effects
I want an effect like this graph (also similar to the effect seen on Bien JouƩ):

You can see that the further a point is from the center point, the greater the distortion, thus the need to calculate the distance between each image and the center point. You can see my demo for a better visualization.
Shader is a complicated topic, so Iāll not be going into the detailed shader implementation. For anyone thatās interested, please refer to my shader file.
Conclusion
Since I have mentioned that Iāll only be taking the core problems on a concept level, please forgive me if I have skipped too much on the technical aspect. All in all, it was a fun journey exploring how to build up a solution for an infinite gallery with r3f. The result came out better than I expected, though some performance issues are still around.
If you are interested in the details of my implementation, please refer to my Github repo.
