You can do a lot of crazy stuff with JavaScript. But can you re-render a website inside an iframe inside that same website? You know, something like this:
Something like this, but with the current website inside the laptop. Photo Source.
Don’t know why I thought of this, but the idea started to grow on me. I knew it could be done. I knew it would look trippy. So I decided to bring the idea to life.
“What is the most resilient parasite? Bacteria? A virus? An intestinal worm? An idea. Resilient… highly contagious. Once an idea has taken hold of the brain it’s almost impossible to eradicate. An idea that is fully formed - fully understood - that sticks; right in there somewhere.” - Cobb (Leonardo DiCaprio in Inception)
Found out there’s a name for this kind of effect - Droste effect.
The Droste Effect.
How would one create such an effect using JavaScript? My first approach was to take a screenshot of the current website, and just put it on display inside the mockup photo. I tried out a few libraries that take screenshots of the current page (like this one), but I could never get them to work 100%.
My next approach was to insert iframes inside the mockup photos. This turned out to be more feasible, and after working on this approach for a while I decided to turn it into a small JavaScript library - inception.js. Here’s a quick demo:
Basic example
Lets say we have a container div, and want to insert the current page inside it. Using inception.js, all we need to do is:
inception1 = new Inception({
parentElementId: "iframeContainer1",
levels: 3
})
And the result is:
That’s it. The number of nested iframes is set by the ‘levels’ parameter.
Okay I lied. The code I used was little bit different. If you manage to scroll down inside those iframes, you’ll see there are no disqus comments at the end of the iframes. I didn’t include them because I always get nasty errors when using disqus stuff. I didn’t want those errors to occur again inside the iframes, so I removed all the disqus stuff from the iframes. To do this, I added a class called “disqus_stuff” to all the disqus script elements, and added ‘classesToRemove: “disqus_stuff”’ to the Inception constructor call.
inception1 = new Inception({
parentElementId: "iframeContainer1",
levels: 4,
classesToRemove: "disqus_stuff"
})
But that’s a pretty bare bones example. To recreate the droste effect in a mockup photo, we need a mockup photo, and some way to insert our iframes on top of that photo.
Mockup photo downloaded from Magic Mockups
The code to recreate this example is
inception2 = new Inception({
imageId: "img1",
levels: 2,
width: "60%",
height: "56%",
top: "25%",
left: "10%",
rotate: "5deg",
classesToRemove: "disqus_stuff",
matchWidthElementId: "img1",
leftAlignWith: "img1"
}
The only difficult part here is getting the width, height, top, left and rotate values for things to work (i.e. positioning the iframes inside the mockup image). But don’t worry, I got you covered. I made a codepen where you can get all the necessary values to set everything up.
What if the screen on the mockup image is tilted in a weird way? Or the screen is not directly facing forward? Franklin Ta has already solved that problem for us. He used matrix3d transforms to place anything inside such screens.
Theoretically, you could always use a matrix3d to place anything on top of mockup images (instead of specifying width, rotation, etc…), but I’d reccomend against using matrix3d’s if you can. Code will look cleaner, it’s easier to set up correctly, and in my experience, the browser has a hard time computing matrix3d transforms.
Here’s and example with matrix3d:
Mockup photo downloaded from Magic Mockups
Code:
inception3 = new Inception({
imageId: "img2",
levels: 1,
matrix3d: "matrix3d(0.326055, ...)",
imageWidth: "960px",
imageHeight: "640px",
classesToRemove: ["disqus_stuff", "nav"],
matchWidthElementId: "img2",
leftAlignWith: "img2",
}
Again, the hard part here is getting the correct matrix3d values for your mockup image. I’ve forked Franklin Ta’s codepen and modified it slightly to facilitate usage with mockup images.
Finally, I’d like to say that getting this to run on every page can be a real challenge. Certain things will not work. For example, 3D translations may screw up the z-index of some elements, and certain elements inside the iframes will not render (on Chrome, Firefox works fine?). Not only that, I haven’t done anything to optimize the code. It’s a mess. Seriously. If you open Chrome’s dev console and run an audit you’ll see what I mean.
Looks pretty tight though. More on Github.