Interactive Layer 2 NFTs | A Basic Guide Into Creating HTML/JS NFTs

purplering741
9 min readMay 31, 2022

--

As quite a few of you were asking for a guide on how to mint interactive NFTs on Loopring Layer 2 and the resources out there are quite scarce, i decided to write down a very basic tutorial for a simple interactive NFT!

So the secret ingredient to interactive functionality in NFTs is simply HTML/JS and some sprinkles of magic. That’s it. If you are a web developer, coding wizard or have already some experience coding websites you can probably skip all the following and get started. Happy hacking! ❤

If you have no idea what HTML, JavaScript or CSS is, i’d recommend to study at least the very basics over at w3schools.com. There are some excellent tutorials and examples for your first steps in web development! Command line experience helps as well, you should also be able to do this without touching it.

If you even just have a basic understanding of all this stuff, great! We can get started! 🤓

In this tutorial i will show you how to prepare all the necessary files. For the minting process itself there are plenty tutorials out there so i won’t go into the the details of that. 🙌

Ok so we need an idea first… 🤔 why not make the NFT have a random background color which changes on click? Would be nice to also display the color code in a label… I’ll name it ‘Random Color Picker’!

You can check the final result here: https://lexplorer.io/nfts/0xfd9381101d013414809a9840d6d8814aa533fbc7-0-0xb5212b4dde0cacc61e2ab886f3005837a841f32e-0xd54dedc02c521d4d42f633b4ef1b676b26ae25465ef85ba79ff28296442e25e1-7

Preview

Alright, let’s create a new folder for the NFT…

$ mkdir random-color-picker

… and open it:

$ cd random-color-picker

You can name this whatever you like!

It makes sense to store the thumbnail and metadata here too, however you can’t upload it all at once since you need to grab the image and animation_url CIDs first before uploading the metadata JSON!

Create a folder for the source code of your project in the random-color-picker directory:

$ mkdir src

Create an index.html file in the src directory, this will be the entry point to your NFT!

$ touch src/index.html

I guess at this point it is worth mentioning that the content will be served in an iframe (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe). It is essentially a sandbox for your content, in short this means not everything that works on a “normal” webpage would work in an iframe too. Relative imports e.g. for script and style files are allowed. Please read up on the restrictions!

(This might be something for an advanced tutorial but if you are doing some fancy JS stuff make sure you are testing your content in an iframe first before minting! Via devtools you can check how the iframes of lexplorer.io and the GameStop wallet are configured. Create a preview.html with those iframes pointing to your index.html for testing on a local web server!)

Double click on the ìndex.html file, your browser should open up and display a white page.

So far so good!

Let’s create the basic HTML structure needed. I use VisualStudio Code which has this handy code completion shortcut: html:5. Set the content of the <title> tag to the name of your NFT.

You don’t need the comments in your code! (<! — some comment →)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Random Color Picker</title> <!-- Name of the NFT -->
</head>
<body>
...
</body>
</html>

Refresh your browser, you should see the name of your NFT in the browser tab! 🎉

It’s not really necessary to put the CSS and JS code into separate files for such a simple setup. However when your project grows you better have some code separation, so let’s create two new files for our styles and script in the src directory!

$ touch src/style.css
$ touch src/script.js

Next we need to link them in the ìndex.html. Put the styles in the bottom of the <head> so the browser already has them for styling when processing the rest of the HTML, the script goes at the end of the <body> so we can access the HTML elements when it is executed.

(For advanced code it’s advised to listen for DOM ready etc. in your code but then again you probably already know what you’re doing! For this example we can Keep It Simple, Stupid! 😜)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Random Color Picker</title>
<link rel="stylesheet" href="style.css"> <!-- CSS styles of the NFT -->
</head>
<body>
...
<script src="script.js"></script> <!-- JS script of the NFT -->
</body>
</html>

Finally we just need a label that will show the current color code. Let’s put a <span> element into the <body> and give it an id. This allows it to be accessed by the script.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Random Color Picker</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<span id="color-code"></span> <!-- Label for the current color code -->
<script src="script.js"></script>
</body>
</html>

If you refresh the page now not much will have changed at first glance. We will add some styling in style.css and quickly test if the script.js is properly loaded (of course you could also check this in dev tools network tab!).

Add the following content to style.css. We reset the default browser margin and padding on the html and body elements. To have something more visual the background color of the body is set to our favourite color! 💜

body,
html {
margin: 0;
padding: 0;
}
body {
background-color: purple;
}

Add the following line to script.js:

alert('BUY HOLD DRS!');

If you’ve done everything correctly so far, you should now have a nicely coloured page with an alert popping up after the next refresh!

So far so bueno, let’s add some interaction!

We want to:

  1. Set a random background color when the NFT was opened and each time it is clicked.
  2. Update the label when the color was changed.

In scripts.js we create a new function called setRandomColor, let’s show the alert every time the function is run!

The first three lines are the function declaration which just defines what it should do. In order to set a random color on load we simply execute it via setRandomColor(). This will run the function once on the first page load. Refresh your browser and you should see the alert again. This time though is was called from within your new function!

function setRandomColor() {
alert('BUY HOLD DRS!');
}
setRandomColor();

When clicking the body nothing happens though.. 😔

Let’s add an event listener to the body element:

document.body.addEventListener(type, listener)

It should register ‘click’ events and execute the setRandomColor function so let’s add that to the script.js!

function setRandomColor() {
alert('BUY HOLD DRS!');
}
setRandomColor();document.body.addEventListener('click', setRandomColor);

Refresh the browser, try to click the body and… wait, what? No alert? 😨

Well the event was registered but if you inspect the HTML structure in dev tools you will see that the body has a height of 0px which makes it not exactly easy to click..

Let’s change that and update the style.css:

body,
html {
margin: 0;
padding: 0;
}
body {
height: 100vh;
width: 100vw;
background-color: purple;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
}

We changed the body to have the same height and width as the viewport. Also let’s already update the font to look a little nicer for the label we’ll add later.

Now the click event should properly register where ever you click on the page, great success! 🎉

Next we just need to generate a random color in our function, update the body background and set the label text!

First we generate a random hex color code with some cool one-liner, shamelessly stole the idea from here: https://dev.to/akhil_001/generating-random-color-with-single-line-of-js-code-fhj 💜.
We grab the label span element via the id we defined in the markup. Next the generated color code is assigned to the background of the body element.
The textContent of the label is set to the hex code in uppercase just to make it look a little nicer.

function setRandomColor() {
var randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
var colorCodeLabel = document.getElementById('color-code');

document.body.style.backgroundColor = randomColor;
colorCodeLabel.textContent = randomColor.toUpperCase();
}

Now each time you click the body a new color will be shown. The label in the top left corner will display the respective hex code.

Let’s also make the label look a little more polished and move it to the bottom middle of the screen via flex. Adding some CSS transition of 2 seconds on the background will make the color change look much better. During the transition we will fade out the label, update the text and then fade it back in.

body,
html {
margin: 0;
padding: 0;
}
body {
height: 100vh;
width: 100vw;
background-color: black;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
transition: background 2s;
display: flex;
align-items: flex-end;
justify-content: center;
}
#color-code {
opacity: 0;
transition: opacity 1s;
margin-bottom: 12px;
padding: 8px 12px;
border-radius: 8px;
background-color: #333;
color: white;
}
#color-code.show {
opacity: 1;
}

Right now the label will not show up at all anymore as we added a helper class .show for toggling it. Let’s update the JS. In setRandomColor we first remove .show from the label which will fade it out in 1 second. We wait for the same time (setTimeout with 1000ms), update the text and add .show which will fade the label back in.
This all takes (more or less :P) 2 seconds, the same transition time we set for the background color.

And.. yeah. That’s it for the code! 🤷‍♂️

You should now have the three files at the bottom of this post in the src directory. Now you simply need to upload the src folder to IPFS. The ànimation_url of your metadata should point to the CID of the folder, no need to add /index.html! Use any thumbnail you like for the image property. :)

For this example i set up this very proficient image in Gimp:

NFT Thumbnail

I’m excited to see what cool stuff you come up with! As said in the beginning this is very basic. There are many more possibilities! Also this is rather quick and dirty, please stick to clean code principles and don’t copy my style! :D

A final note on “dynamic” Layer 2 NFTs…

While this approach also allows for fetching off-chain data, it’s not very much recommend as you are dependent on the availability of the external api. It’s not truly dynamic without an oracle! I prefer the term “fauxnamic” that popped up on discord! :)

There is no way to update your code once it’s uploaded to IPFS, so please make sure to add a fallback in case no data can be fetched! E.g. for my Gas Fee NFT it will show the default green animation if no data could be fetched from etherchain.org, on click the gas fee label will show an error message. Not great, not terrible! Just the best we can do right now without zkEVM…!

Note on external libs: I used a little more advanced stuff to browserify the 2D-physics lib matter-js into the code of the “The Purple Rain” interactive NFT and minify all code. Best to also run your code through babel if you use ES6 features. If you can still follow, you most likely know what you’re doing anyway though.. 🤓

Here’s the complete code! 💙

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Random Color Picker</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<span id="color-code"></span>
<script src="script.js"></script>
</body>
</html>

script.js

function setRandomColor() {
var randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
var colorCodeLabel = document.getElementById('color-code');
colorCodeLabel.classList.remove('show');document.body.style.backgroundColor = randomColor;setTimeout(() => {
colorCodeLabel.textContent = randomColor.toUpperCase();
colorCodeLabel.classList.add('show');
}, 1000);
}
setRandomColor();document.body.addEventListener('click', setRandomColor);

style.css

This was run through autoprefixer: https://autoprefixer.github.io/ to include all vendor prefixes. Not entirely sure if necessary in that context but best practice! 🙌

body,
html {
margin: 0;
padding: 0;
}
body {
height: 100vh;
width: 100vw;
background-color: black;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
-webkit-transition: background 2s;
-o-transition: background 2s;
transition: background 2s;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: end;
-ms-flex-align: end;
align-items: flex-end;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
#color-code {
opacity: 0;
-webkit-transition: opacity 1s;
-o-transition: opacity 1s;
transition: opacity 1s;
margin-bottom: 12px;
padding: 8px 12px;
border-radius: 8px;
background-color: #333;
color: white;
}
#color-code.show {
opacity: 1;
}

Thank you for attention and happy coding!

If you like my work and want to support it, I’m delighted about every vote and comment! 🙏

Thanks folks and love y’all! 💜

--

--