Try   HackMD

Feral File Artwork Library

tags: Feral File operation

Feral File Variables

For Feral File generative artworks, it injects pre-defined variables then the collector click to view the software artwork in in frame. This helps artists be able to make variants to their artworks. The variables are:

  • blockchain - (bitmark | ethereum | tezos)
  • contract - contract address if any. empty for bitmark
  • token_id - a unique id of a token in the blockchain. It is hex in bitmark and decimal in tezos and ethereum
  • artwork_number - the artwork number of a series starts from 1??
  • token_id_hash - the sha256 hash of a token ID.
  • edition_number - the edition number of an index starts from zero (legacy only)

Token ID

By the design of token contract, it requires a unique id for each tokens. However, in our exhibition structure, we have multiple series and each series would have its own artwork with a same index starts from 0. This brings duplication of token IDs to a contract if we simply use the index as token ID. To avoid confliction, we create a function to generate deterministic unique token id by combining series id and token indexes.

Token ID Hash

It is the sha256 hash of a token ID. The value of the hash is 32 bytes and represented in hex. Here is an example of the hash

0x0aaa657eb7727d08721d447e53ae2959a5a4c6e4a061cc21724a6f759d025d4b

which is a 64-length hex string and is prefixed with 0x.

Deterministic Randomness

To provide randomness to generative artworks, we create a snippet to offer a random function based on sfc32. The function takes token_id_hash (defined above) as the seed of the randomness. This ensures that the randomness is deterministic. If the token_id_hash is not given, the snippet will create a random hash so we can test loacally.

We use sfc32 in our random function. sfc stands for "Small Fast Counter". It runs very fast in JS. It is part of PractRand random number testing suite.

Code

<script id="ffhash-snippet">
  function randomHash() {
    return '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'.replace(/[f]/g, function (c) {
      let r = Math.random() * 16 | 0;
      return r.toString(16);
    });
  }

  const urlSearchParams = new URLSearchParams(window.location.search);
  const params = Object.fromEntries(urlSearchParams.entries());
  let hash = params.token_id_hash || randomHash()
  console.log(hash)

  {
    let sfc32 = function (uint128Hex) {
      let a = parseInt(uint128Hex.substr(0, 8), 16);
      let b = parseInt(uint128Hex.substr(8, 8), 16);
      let c = parseInt(uint128Hex.substr(16, 8), 16);
      let d = parseInt(uint128Hex.substr(24, 8), 16);
      let rand = function () {
        a |= 0;
        b |= 0;
        c |= 0;
        d |= 0;
        let t = (((a + b) | 0) + d) | 0;
        d = (d + 1) | 0;
        a = b ^ (b >>> 9);
        b = (c + (c << 3)) | 0;
        c = (c << 21) | (c >>> 11);
        c = (c + t) | 0;
        return (t >>> 0) / 4294967296;
      };
      for (let i = 0; i < 1e6; i += 2) {
        rand();
      }
      return rand
    };

    var ffrand = sfc32(hash.substr(34, 32))
  }
</script>

Usage

After you load the snippet, simply call ffrand to get a random number.

<script>
  let r = ffrand()
</script>

If you would like to design the randomness by yourself, please ensure the random value is deterministic by a given token_id_hash. Otherwise, the artwork would be different by each page reloads.

Artwork Library

With this parameters from FF, we come up with a library that can get provenance from our indexer. Thus, for software artwork developer's, they can create artwork variants by integrating this library.

Learning from fxhash, this library is designed to be a simple snippet script (library) that can be put on the top of generative artworks.

In this snippet script, we only use XHR(XMLHttpRequest). It is because the library does not require any other third-party libraries. This minimize the dependencies of artworks for artists.

Function

Here lists supported functions

  • Get blockchain height (integer)
  • Get provenance (array)

Usage

Attach the script in HEAD. And call to ffinit on page loaded.

<!DOCTYPE html>
<html lang="en">
<head>
  <script id="ff-library" 
  src="https://ipfs.bitmark.com/ipfs/QmRfJutL1FyAKqT2vAAPErgcJqTfCyWvQtnbiShZhTkNL4"
  type="text/javascript"></script>
</head>

<body onload="ffinit()"></body>

</html>

In your script, you are able to get these events:

  • provenance-request-error
  • provenance-ready
  • blockchain-info-request-error
  • blockchain-info-ready

Example:

 <script>
    // listen to blockchain information data
    window.addEventListener("blockchain-info-ready", function (event) {
      console.log("current block height:", event.detail.height);
    })

    window.addEventListener("blockchain-info-request-error", function (event) {
      console.log(event.detail.error)
    })

    // listen to token provenance data
    window.addEventListener("provenance-ready", function (event) {
      console.log("provenance length:", event.detail.provenances.length)
    })

    window.addEventListener("provenance-request-error", function (event) {
      console.log("fail to get provenance:", event.detail.error)
    })
  </script>

Provenance Data Example

[
    {
        "type": "transfer",
        "owner": "KT1GbyoDi7H1sfXmimXpptZJuCdHMh66WS9u",
        "blockchain": "tezos",
        "blockNumber": 2498727,
        "timestamp": "2022-07-02T05:29:14Z",
        "txid": "ooHk3jYeGuSM2ruCPUog5QprLqphjDrNnDNUBoY2UZDWbm14Unf",
        "txURL": "https://tzkt.io/ooHk3jYeGuSM2ruCPUog5QprLqphjDrNnDNUBoY2UZDWbm14Unf"
    },
    {
        "type": "mint",
        "owner": "tz1Ne3XbbFpWAZjPf8QJy5fbuRWpcwUyg42X",
        "blockNumber": 2498678,
        "timestamp": "2022-07-02T05:04:44Z",
        "txid": "oo3X4Fy9RADVk6JVcKS1BeJLjB6bRzwTxLKvbBjFDBdPabHrcmJ",
        "txURL": "https://tzkt.io/oo3X4Fy9RADVk6JVcKS1BeJLjB6bRzwTxLKvbBjFDBdPabHrcmJ"
    }
]

Artwork Attributes

To provide function tracking artwork attributes, we create a snippet to offer a $feralfile namespace and 2 functions $feralfile.features(array_of_attributes) to input artwork attributes and $feralfile.getFeatures() to get saved attributes based on artwork information

Code

<script id="feralfile-attributes">
  var $feralfile = {
      features: function (attrs) {
        this.attributes = attrs || this.attributes;
      },
      getFeatures: function () {
        return this.attributes || [];
      },
    };
</script>

Usage

After you load the snippet, simply call $feralfile.features() to set attributes.

<!DOCTYPE html>
<html lang="en">
<head>
  <script id="ff-attrs" 
  src="https://ipfs.bitmark.com/ipfs/QmakVUXKyHF5cddi6owHLbCib7wbU36nY2JoQiNkE7eSho"
  type="text/javascript"></script>
</head>

<body>
    <script>
      $feralfile.features([
        {
          name: 'color',
          value: 'dark',
        },
        {
          name: 'tmax',
          value: 254,
        },
        {
          name: 'tmin',
          value: 189,
        },
        {
          name: 'letter',
          value: 5,
        },
        {
          name: 'dotnum',
          value: 374,
        },
      ]);
    </script>    
</body>

</html>

TODO

  • Refactor API calls
  • Merge events
  • Select to enable watching

Reference

Source Code