Creating on-chain NFTs on Cardano
This guide brings together information I learned in creating my first on-chain collection, O. Community members I reached out to, namely Hookman (Block Clocks) and ThisCrazyLife (The Refresh), were tirelessly helpful throughout the process and creating this guide is my way of putting this shared knowledge to further use and sharing what I learned in the process.
To start, you will need an html file containing some basic css to ensure that your NFT will display correctly. Below the code snippet you will find descriptions of each of the elements used and the reasons why.
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<canvas id='myCanvas' style='object-fit: contain; width: 100vw; height: 100vh;'>
Your browser does not support the HTML5 canvas tag.</canvas>
var canvas = document.getElementById('myCanvas');
canvas.width = 4096;
canvas.height = 4096;
canvas.style.width = '100vw';
canvas.style.height = '100vh';
var ctx = canvas.getContext('2d');
var cW = ctx.canvas.width;
var cH = ctx.canvas.height;
The <style> section ensures that there is no blank space shown around your image by defining padding and margin as 0. It is also likely that scrollbars in the finished piece are undesirable, which is why we define overflow:hidden.
Up next is the script element where we first define a few variables which will help with the NFT coding process. canvas.width and canvas.height are set here to produce a square 4k image and you may adjust these as you see fit. I would advise considering how and where your pieces are most likely to be seen if you haven’t already decided on the size of your image. If you are looking at print sizing note that output will be at 96dpi and a 4k square image will print to 108 x 108cm without any pixelation.
Following the size definition we style the height and width using vh (viewport height) and vw (viewport width) values which ensure the image fills the viewport responsively. The number preceding vh and vw is the percentage you would like to fill the viewer by, hence 100vw and 100vh will fill 100% of the viewable width and height ensuring the image won’t extend beyond the viewable bounds of the window by automatically resizing.
We then state some shorthand variables for the canvas (ctx), canvas width (cW) and canvas height (cH) to ensure your code can be kept as light as possible. You may also change these, but be sure to use the same variable names throughout your code.
Coding your NFT
Since we are displaying the NFT responsively, you should think about coding responsively. This is where cW and cH come in useful because rather than defining a set size and weight for elements, a responsive piece will have objects sized in relation to the size of the canvas. For example, a circle centred on the canvas with a radius 1/4 of the canvas width might be coded as follows:
ctx.lineWidth = canvasW * 0.00333;
ctx.arc(cW / 2, cH / 2, cW / 4, 0, 2*Math.PI);
Note that I have also set the line width or stroke weight in relation to the canvas to ensure proper scaling.
Be sure to check the results of your coding regularly. Using the aforementioned Live Server extension in conjunction with VS Code or simply double clicking the html file in explorer to open the in your browser of choice are great ways of doing so (note that the latter method will not update as you add to your code).
I recommend making a copy of your original html file here, just in case anything goes wrong. From here you can copy all of the code between the elements (and not the elements themselves), paste them into an online minifier, minify the script, copy the result and paste it in place of the script you copied.
My personal preference is for Toptal’s online tool
, but many others are available.
Conversion to base64
For our metadata purposes we also need to ensure that no single string is longer than 64 characters, which means we will need to break up the base64.
Online encoders are available, however this Python script, which I have made available via Google Colaboratory
provides something of a shortcut, encoding the document and breaking it into strings not longer than 60 characters, inside an array, ready to be pasted into your metadata.
Compiling the metadata
The metadata file will take the form of a JSON and it will contain your base64 array and everything you want to tell the world about your NFT.
Please refer to the CIP 25 NFT Metadata Standard
for guidance on setting up your metadata. The following takes the general structure outlined in the standard and shows the mediaType which should be stated and where your base64 array should be included (note the “data:text/html;base64,” prefix which is automatically generated by the Python script linked above):
"image": <uri | array>,
"description": <string | array>,
If you wish to include an IPFS thumbnail image, simply replace <other properties> with the following:
"image": <IPFS link>,
To make sure the metadata and piece are displaying correctly, I strongly recommend using the pool.pm metadata test page. You can experiment with re-ordering items to some extent by capitalising the start of a field name (such as “Name” rather than “name”). Capitalised fields will display before others, in alphabetical order. The metadata file can then be saved in a text document with the extension .json
Congratulations, you’re now ready to mint your on-chain NFT.