🥷The PFP

Diving Into the Code for Each Ninja PFP

The code behind each Ninja inscription is remarkably compact, at less than 1 KB in size when minified. However, this simplicity belies their complexity; each inscription embodies a depth that we are immensely proud of. In fact, by sharing everything in this gitbook, we hope to help future projects push innovation on Bitcoin Ordinals even further.

Let's dig in to an example:

<script id="ord-root"
    src="assets/draw.js"
    data-a="assets/test-animation.json"
    data-l='[
        {
            "color": "#00a38b",
            "url": "/demo/Stoic Body___Stoic Body White.svg"
        },
        {
            "color": "#00a38b",
            "url": "/demo/Ninjalerts Head___Ninjalerts Head White.svg"
        },
        {
            "url": "/demo/Ninjalerts Face___Classic.svg"
        },
        {
            "url": "/demo/Top of Head___Crown.svg"
        }
    ]'>
</script>

Overview

In the code sample above, you will see three main parts to the Ninja inscription.

  1. draw.js — This is the beast that does most of the work for each Ninja at just over 1100 lines of code. It loads everything, handles all the file conversions, menu, animation, stickers, resizing, etc. In a future version, we plan to modularize all of its features so it can be re-used in an easy way by other projects.

  2. test-animation.json — The default bouncing head animation that comes with each Ninja. this JSON is used by draw.js and converted into a GIF from within the Ninja menu, for use on Discord or other social platforms that allow GIFs as avatars.

  3. Ninja Traits - The final JSON array starting with "data-l" contains each Ninja trait as layers. These are imported as SVGs with colors defined inline. This allows us to use the same SVG trait for unlimited color variations.

Below we dig into each one of these codebases 1 by 1.

Please note that what we inscribe on-chain is actually a minified version of each one of these files, in order to save on block space and network fees.

Draw.js

As you will see below, this file is a monolith containing all the code to create the Ninja PFP and do all of the cool features packed into it. After the release on Ninjas v1, we plan to break up this file into multiple modules so they can easily be leveraged by any project, via recursion.

(Full code posted once inscribed)

(async function () {
    const helpers = {
        getQueryParam: (name) => {
            const urlParams = new URLSearchParams(window.location.search);
            return urlParams.get(name);
        },
        clear: () => {
            context.clearRect(0, 0, width, height);
        },
        showLoading: () => {
            loader.style.display = 'flex';
        },
        isValidJson: (str) => {
            try {
                JSON.parse(str);
            } catch (e) {
                return false;
            }
            return true;
        },
        isValidAnimation: (jsonString) => {
            const frameMatches = jsonString.match(/"frame_\d+":/g);
            const layerPropertyMatches = jsonString.match(/"layer_\d+":\s*{[^}]*}/g);
            let json;
            try {
                json = JSON.parse(jsonString);
            } catch (e) {
                animationInvalidMessage.innerText = "Invalid JSON string";
                return false;
            }
            if (frameMatches) {
                const uniqueFrames = new Set(frameMatches);
                if (uniqueFrames.size !== frameMatches.length) {
                    animationInvalidMessage.innerText = "Duplicate frame keys found in JSON string";
                    return false;
                }
            }
            if (layerPropertyMatches) {
                for (const match of layerPropertyMatches) {
                    const propertyMatches = match.match(/"\w+":/g);
                    const uniqueProperties = new Set(propertyMatches);
                    if (uniqueProperties.size !== propertyMatches.length) {
                        animationInvalidMessage.innerText = "Duplicate properties found in layer object";
                        return false;
                    }
                }
            }
            for (const frameKey in json) {
                if (!/^frame_\d+$/.test(frameKey)) {
                    animationInvalidMessage.innerText = `Invalid frame key format: ${frameKey}`;
                    return false;
                }

Test-Animation.json

For the Ninja animation, we built an engine that can process any custom JSON input. This allows anyone to animate their Ninja however they like, with an easy to understand syntax. This is the JSON for the default animation, with the Ninja bouncing up and down for Discord calls.

You can view the Test-Animation.json code in the section about the Animation API.

Individual Traits as SVGs

Here's what the code for "Stoic Body__Stoic Body White.svg" looks like. The %%COLOR%% tag is used to customize the color of this SVG so we only need to inscribe one version of this trait in order to produce unlimited color version.

<svg viewBox="0 0 1000 1000" version="1.1" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
    <path fill="#000000" d=" M 519.22 711.17 C 541.47 709.52 563.94 710.13 585.96 713.93 C 617.55 719.47 648.11 734.25 670.04 757.96 C 689.33 778.75 700.83 805.57 707.01 832.99 C 713.17 859.97 715.08 887.67 716.76 915.23 C 718.49 943.44 719.12 971.73 718.71 1000.00 L 699.58 1000.00 C 699.60 976.31 698.89 952.64 697.95 928.97 C 696.34 897.03 694.71 864.82 687.21 833.60 C 680.66 806.98 668.53 780.74 647.81 762.18 C 635.31 750.85 620.15 742.54 603.94 737.88 C 580.94 731.18 556.75 730.50 532.97 731.11 C 490.30 732.86 447.82 737.96 405.71 744.94 C 383.11 748.90 360.22 752.89 339.00 761.96 C 328.23 766.51 318.02 772.50 309.17 780.18 C 294.96 792.26 284.37 808.21 277.36 825.41 C 270.23 843.54 266.67 862.84 264.31 882.12 C 260.28 914.93 260.76 948.04 261.06 981.03 C 261.47 987.34 261.41 993.69 262.04 1000.00 L 238.69 1000.00 C 239.02 980.99 240.12 962.01 240.69 943.02 C 242.16 902.30 242.92 860.73 255.67 821.63 C 261.52 803.83 269.79 786.45 282.43 772.43 C 293.31 760.28 307.46 751.33 322.70 745.68 C 341.30 738.73 360.65 734.05 380.02 729.83 C 425.91 720.37 472.53 714.78 519.22 711.17 Z" />
    <path fill="#000000" d=" M 589.53 856.41 C 590.27 851.76 596.58 849.97 599.95 853.04 C 616.37 866.91 625.07 887.70 629.53 908.27 C 635.67 938.44 634.47 969.39 634.80 1000.00 L 621.36 1000.00 C 621.46 983.68 620.93 967.37 620.24 951.07 C 619.32 935.89 618.04 920.62 614.10 905.88 C 610.35 891.88 604.08 878.41 594.73 867.26 C 592.33 864.10 588.93 860.71 589.53 856.41 Z" />
    <path fill="#000000" d=" M 388.88 857.86 C 390.79 855.93 392.98 853.62 395.94 853.77 C 399.42 853.88 402.18 857.47 401.41 860.87 C 400.40 864.56 397.61 867.37 395.35 870.35 C 389.73 877.06 385.23 884.69 381.94 892.81 C 374.16 912.19 374.22 933.44 374.17 954.00 C 374.03 969.34 374.65 984.66 374.68 1000.00 L 361.34 1000.00 C 360.76 990.69 360.95 981.36 360.49 972.05 C 360.38 950.79 359.79 929.34 363.47 908.30 C 366.71 889.49 375.42 871.49 388.88 857.86 Z" />
    <path fill="%%COLOR%%" d=" M 405.71 744.94 C 447.82 737.96 490.30 732.86 532.97 731.11 C 556.75 730.50 580.94 731.18 603.94 737.88 C 620.15 742.54 635.31 750.85 647.81 762.18 C 668.53 780.74 680.66 806.98 687.21 833.60 C 694.71 864.82 696.34 897.03 697.95 928.97 C 698.89 952.64 699.60 976.31 699.58 1000.00 L 634.80 1000.00 C 634.47 969.39 635.67 938.44 629.53 908.27 C 625.07 887.70 616.37 866.91 599.95 853.04 C 596.58 849.97 590.27 851.76 589.53 856.41 C 588.93 860.71 592.33 864.10 594.73 867.26 C 604.08 878.41 610.35 891.88 614.10 905.88 C 618.04 920.62 619.32 935.89 620.24 951.07 C 620.93 967.37 621.46 983.68 621.36 1000.00 L 374.68 1000.00 C 374.65 984.66 374.03 969.34 374.17 954.00 C 374.22 933.44 374.16 912.19 381.94 892.81 C 385.23 884.69 389.73 877.06 395.35 870.35 C 397.61 867.37 400.40 864.56 401.41 860.87 C 402.18 857.47 399.42 853.88 395.94 853.77 C 392.98 853.62 390.79 855.93 388.88 857.86 C 375.42 871.49 366.71 889.49 363.47 908.30 C 359.79 929.34 360.38 950.79 360.49 972.05 C 360.95 981.36 360.76 990.69 361.34 1000.00 L 262.04 1000.00 C 261.41 993.69 261.47 987.34 261.06 981.03 C 260.76 948.04 260.28 914.93 264.31 882.12 C 266.67 862.84 270.23 843.54 277.36 825.41 C 284.37 808.21 294.96 792.26 309.17 780.18 C 318.02 772.50 328.23 766.51 339.00 761.96 C 360.22 752.89 383.11 748.90 405.71 744.94 Z" />
</svg>

Last updated