{"id":13456499,"url":"https://github.com/nftchef/art-engine","last_synced_at":"2025-03-24T10:32:50.233Z","repository":{"id":38616999,"uuid":"414723280","full_name":"nftchef/art-engine","owner":"nftchef","description":"An advanced fork of the HashLips Art Engine with additional features to generate complex art from provided layers using the familiar Hashlips setup.","archived":false,"fork":true,"pushed_at":"2024-01-17T11:41:52.000Z","size":11000,"stargazers_count":427,"open_issues_count":12,"forks_count":203,"subscribers_count":20,"default_branch":"main","last_synced_at":"2024-10-29T00:32:14.020Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"HashLips/hashlips_art_engine","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nftchef.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-10-07T18:58:02.000Z","updated_at":"2024-10-03T03:29:00.000Z","dependencies_parsed_at":"2023-02-12T09:31:09.810Z","dependency_job_id":null,"html_url":"https://github.com/nftchef/art-engine","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nftchef%2Fart-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nftchef%2Fart-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nftchef%2Fart-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nftchef%2Fart-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nftchef","download_url":"https://codeload.github.com/nftchef/art-engine/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245252524,"owners_count":20585085,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T08:01:23.112Z","updated_at":"2025-03-24T10:32:49.728Z","avatar_url":"https://github.com/nftchef.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# DOCUMENTATION https://generator.nftchef.dev/\n\n# FAQ, general questions, and help\n\nPlease check the discussions for similar issues or open a new discussion.\n\n## https://github.com/nftchef/art-engine/discussions\n\n#### You can find me on twitter or Discord,\n\n- Twitter: https://twitter.com/nftchef\n- Discord genkihagata#3074\n\n## ❤️ Support This generator ❤️\n\nEth address: `0xeB23ecf1fa9911fca08ecAbe83d426b6bd525bB0`\n\n# Features\n\n_This repository is a hard-fork from the original Hashlips generator (c.2021) and makes a couple of **fundamental changes** to how layers are expected to be named and organized. Please read this README's Nesting Structure section and the related Advanced options for a better understanding of how things should be named and organized._\n\n## Nested Structures\n\n- [Nested Layer Support and Trait Type definition modification/branch](#nested-layer-support-and-trait-type-definition-modification-branch)\n  - [Example](#example)\n  - [Nesting structure](#nesting-structure)\n    - [Advanced options](#advanced-options)\n      - [Required files](#required-files)\n      - [Sublayer options](#sublayer-options)\n- [Metadata Name + Number](#metadata-name-and-number)\n\n## Options and conditional output\n\n- [Chance of \"NONE\" or skipping a trait](#Chance-of-\"NONE\"-or-skipping-a-trait)\n- [Controlling layer order (z-index)](#Controlling-layer-order-z-index)\n- [Flagging Incompatible layers](#flagging-incompatible-layers)\n- [Forced Combinations](#forced-combinations)\n- [Output Files as JPEG](#outputting-jpegs)\n- [ Attribute Display Types and Overrides](#attribute-display-types-and-overrides)\n- [ Trait Value Overrides](#trait-value-overrides)\n\n## Other Blockchains\n\n- [Solana](#solana-metadata)\n- [Cardano](#cardano-metadata)\n\n## Utils\n\n- [Provenance Hash Generation](#provenance-hash-generation)\n- [UTIL: Remove traits from Metadata](#Remove-Trait-Util)\n- [Randomly Insert Rare items - Replace Util](#Randomly-Insert-Rare-items---Replace-Util)\n\n### Notes\n\n- [Incompatibilities with original Hashlips](#incompatibilities)\n\n\u003chr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\n# Nested Layer Support and Trait Type definition modification/branch\n\nThis branch of the Hashlips generator builds on the example (v.1.0.6) and allows you to _nest_ sub-folders within your top layer folders, and, optionally gives you a configuration option to overwrite the `trait_type` that is written to the metadata from those layers.\n\n## Example\n\nThe following example (included in this repository) uses multiple `layer_configurations` in `config.js` to generate male and female characters, as follows.\n\n```js\nconst layerConfigurations = [\n  {\n    growEditionSizeTo: 2,\n    layersOrder: [\n      { name: \"Background\" },\n      { name: \"Female Hair\", trait: \"Hair\" },\n    ],\n  },\n  {\n    growEditionSizeTo: 5,\n    layersOrder: [\n      { name: \"Background\" },\n      { name: \"Eyeball\" },\n      { name: \"Male Hair\", trait: \"Hair\" },\n    ],\n  },\n];\n```\n\nThe Hair layers, exist as their own layers in the `layers` directory and use the `trait` key/property to overwrite the output metadata to always look like, the following, regardless of layer folder it is using–so both Male and Female art have a `Hair` trait.\n\n```js\n    {\n        \"trait_type\": \"Hair\",\n        \"value\": \"Rainbow Ombre\"\n      }\n```\n\n## Nesting structure\n\nIn this modified repository, nesting subdirectories is supported and each directory **can** have it's own rarity weight WITH nested weights inside for individual PNG's.\n\n\u003cimg width=\"291\" alt=\"image\" src=\"https://user-images.githubusercontent.com/2608893/136727619-779221c2-0ec1-42a2-a1c6-144ba4587035.png\"\u003e\n\nFor the example above, `Female Hair` can be read as:\n\n\u003e Female Hair layer is required from config -\u003e Randomly select either `common` or `rare` with a respective chance of `70% / 30%`. If Common is chosen, randomly pick between Dark Long (20% chance) or Dark Short (20%)\n\n## Advanced options\n\n### Required files\n\nAdditionally, `png` files that ommit a rarity weight **will be included** always and are considered \"required\".\n\nThis means, that if you need multiple images to construct a single \"trait\", e.g., lines layer and fill layer, you could do the following:\n\n```js\nHAIR\n|-- Special Hair#10\n|----- 1-line-layer.png\n|----- 2-fill-layer.png\n```\n\nWhere the containing folder will define the traits _rarity_ and in the event that it is selected as part of the randomization, BOTH nested images will be included in the final result, in alphabetical oder–hence the 1, 2, numbering.\n\n### Options\n\n#### Exclude a layer from DNA\n\nIf you want to have a layer _ignored_ in the DNA uniqueness check, you can set `bypassDNA: true` in the `options` object. This has the effect of making sure the rest of the traits are unique while not considering the `Background` Layers as traits, for example. The layers _are_ included in the final image.\n\n```js\nlayersOrder: [\n      { name: \"Background\" },\n      { name: \"Background\" ,\n        options: {\n          bypassDNA: false;\n        }\n      },\n```\n\n#### Use Parent folders as trait_value in attributes\n\nIn some projects, you may define a root folder in `layersOrder` only as a starting point in the branching structure and not want to use that folders name as the `trait_value` in the attributes. Rather, nested, weighted subfolders should be the trait_type for example:\n\n```\nlayers/Headware\n├── Cap#10\n│   ├── cap a#6.png\n│   └── cap b#6.png\n└── Hair#10\\\n    ├── hair a#6.png\n    └── hair b#6.png\n\n```\n\nRather than the attribute data always listing `Headware` as the type, you can use `Cap` or `Hair` instead.\n\nSet useRootTraitType`to`false` in config.js to use parent folder names instead of the root.\n\n### Sublayer Options\n\n🧪 BETA FEATURE\n\n#### Rename Sublayer traits\n\nIn the case that your folder names need to include number or anything else that you do not want in the final metadata, you can clean up the `trait_type` by passing in the `trait` option to the sublayerOptions object where the nested folder lives. For example, if we are using a subfolder named `1-SubAccessory` and want to rename it to `Backpack Accessory`, pass the following configuration\n\n```js\n    layersOrder: [\n      {\n        name: \"Back Accessory\",\n        sublayerOptions: {\n          \"1-SubAccessory\": { trait: \"Backpack Accessory\" },\n        },\n      },\n      { name: \"Head\" },\n```\n\n#### Blend and Opacity\n\nBy default, nested folders will inherit `blend` and `opacity` settings from the root-level layer defined in `layersOrder`. When you need to overwrite that on a sublayer-basis (by name of the nested folder, not by filename), you can specify a sublayerOptions object.\n\n```js\nlayersOrder: [\n      { name: \"Clothes\" },\n      {\n        name: \"Bases\",\n        blend: \"destination-over\", // optional\n        sublayerOptions: { Hands: { blend: \"source-over\" } },\n      },\n      { name: \"Holdableitems\" },\n    ],\n```\n\n#### Common Errors\n\nWhen combining multiple sublayer options, remember that each Sublayer is an object that accepts key:value pairs for each valid option above.\n\nFor example, a single layer may have multiple sublayer options in the following format.\n\n```js\nsublayerOptions: {\n  Hands: {\n    trait: 'New trait name',\n    blend: 'source-over',\n    opacity: 0.9,\n  },\n  Face: {\n    trait: 'the face',\n    blend: 'source-over',\n    opacity: 0.9,\n  }\n}\n```\n\n**In the example above**: The intended stacking order is `Base \u003e Clothes \u003e Hand \u003e holdableItem`, because `Hands` are a nested subfolder of `Bases`, this can be tricky. By defining `blend: destination-over` for the Base, and then `source-over` for the Hands, the stacking order can be controlled to draw the Base _under_ the clothes and _then_ the Hand above the clothes.\n\n# Metadata Name and Number\n\nName + Number prefix and reset for configuration sets\n\nIf you are using the generator with multiple `layerConfiguration` objects to generate different species/genders/types, it is possible to add a name prefix and a reset counter for the name, so the token names start at `1` for each type.\n\nfor example, if you are creating multiple animals, and each animal should start with `Animal #1`, but the token numbers should increment as normal, you can use the following `namePrefix` and `resetNameIndex` properties to acheive this.\n\n```js\n    growEditionSizeTo: 10,\n    namePrefix: \"Lion\",\n    resetNameIndex: true, // this will start the Lion count at #1 instead of #6\n    layersOrder: [\n      { name: \"Background\" },\n      { name: \"Eyeball\" },\n      { name: \"Male Hair\", trait: \"Hair\" },\n    ],\n\n```\n\nYou may choose to omit the `resetNameIndex` or set it to false if you would instead like each layer set to use the token (\\_edition) number in the name–it does this by default.\n\n# Chance of \"NONE\" or skipping a trait\n\nIf you would like any given layer or sublayer to have a chance at _rolling_ `NONE`, you can do this by adding a blank PNG to the folder, and giving it the name of `NONE` + the weight you would like to use for it's chance of being chosen–identical to any other layer.\n\n```\nNONE#20.png\n```\n\nBy using this feature, the trait **will not be included in the metadata**. For example, rather than having `trait_type: 'Hat', value: 'NONE\"` in the metadata, it will be skipped.\n\n## You can change the name\n\nIf you need to change the name from \"NONE\" to something else, you can change the following in config.js\n\n```js\n// if you use an empty/transparent file, set the name here.\nconst emptyLayerName = \"NONE\";\n```\n\n⚠️ NOTE: if you _would_ like the trait to appear as \"NONE\", change the empty layer name to something you are _not_ using. e.g., 'unused'.\n\n# Controlling layer order (z-index)\n\nGenerally, layer order is defined by using `layersOrder` in config.js. However, when using nested folders, layer order defaults to alphanumerec sorting (0-9,a-z)\n\nTo manually define stacking order for sublayers (nested folders) use the `z#,` (`z` followed by a positive or negative number, followed bu a `,` commma) prefix for folder names and/or file names.\n\nExample folders\n\n```\n|- z-1,folder\n|-normal folder/\n|-z1,foldername\n|-z2,foldername/\n|-- z-2,image-that-needs-to-go-under-everything.png\n```\n\nFolders and files without a `z#,` prefix default to a z-index of `0`\n\nIf a file has a `z#,` prefix, and it's parent folder(s) does as well, the files z-index will be used, allowing you to overwrite and define an individual's layer order.\n\nLayer order z-indexes are \"global\" meaning the index is relative to ALL OTHER z-indices defined in every folder/file\n\n# Flagging Incompatible layers\n\n\u003e 👉 **For edge cases only** Nested Folders should always be used first and can solve 90% of \"if this, then _not_ that\" use cases.\n\nTo flag certain images that _should never be used with_ another image, for this, you can use the incompatible configuration in `config.js`\n\nTo set incompatible items, in the `incompatible` object, use the layer/images `cleanName` (the name without rarity weight, or .png extension) as a key, and create an array of incompatible layer names (again, clean names). Layers that have space or hyphens in their names should be wrapped in quotes\n\n⚠️ NOTE: Names are expected to be unique. if you have multiple files with the same name, you may accidentally exclude those images.\n\n```js\nconst incompatible = {\n  Red: [\"Dark Long\"],\n  \"Name with spaces\": [\"buzz\",\"rare-Pink-Pompadour\" ]\n  // directory incompatible with directory example\n  White: [\"rare-Pink-Pompadour\"],\n};\n```\n\n⚠️ NOTE: This relies on the layer order to set incompatible DNA sets. For example the key should be the image/layer that comes first (from top to bottom) in the layerConfiguration. in other words, IF the item (KEY) is chosen, then, the generator will know not to pick any of the items in the `[Array]` that it lists.\n\n# Forced Combinations\n\n![10](https://user-images.githubusercontent.com/91582112/138395427-c8642f74-58d1-408b-94d1-7a97dda58d1a.jpg)\n\nWhen you need to force/require two images to work together that are in different root-layer-folders (the layer config), you can use the `forcedCombinations` object in `config.js`.\n\n```js\nconst forcedCombinations = {\n  floral: [\"MetallicShades\", \"Golden Sakura\"],\n};\n```\n\nUsing the _clean Name_ (file name without weight and .png extension), specify the key (if names have spaces, use quotes `\"file name\" :`)\n\nThen, create an array of names that should be required.\n\nNote: the layer order matters here. The key (name on the left) should be a file withing a layer that comes first in the `layersOrder` configuration, then, files that are required (in the array), should be files in layers _afterward_.\n\n### Debugging:\n\nset `debugLogging = true` in config to check whether files are being `set` and `picked` for the forced combinations. You should see output that looks like the following if it is wokring:\n\n![image](https://user-images.githubusercontent.com/91582112/138395944-31032584-f1e5-4b7e-b8c6-c6ad49f0d2ba.png)\n\nIf not, double check your file names, layer ordering, and quotation marks.\n\n# Outputting Jpegs\n\nIf you're working with higher res, it's recommended for your storage-costs-sake to output the image to jpeg, to enable this, set `outputJPEG` in `config.js` to `true`.\n\n```js\nconst outputJPEG = true; // if false, the generator outputs png's\n```\n\n⚠️ NOTE: If you're running an M1 Mac, you may run into issues with canvas outputting jpegs and may require additional libraries (e.g. Cairo) to solve and may not work at this time.\n\n## Attribute Display Types and Overrides\n\nIf you need to add randomized values for traits and different display types supported by OpenSea, this branch re-purposes the `extraAttributes` configuration for that purpose.\n\nin config.js\n\n```js\nconst extraAttributes = () =\u003e ([\n  {\n    // Optionally, if you need to overwrite one of your layers attributes.\n    // You can include the same name as the layer, here, and it will overwrite\n    //\n    \"trait_type\": \"Bottom lid\",\n    value:` Bottom lid # ${Math.random() * 100}`,\n    },\n  {\n    display_type: \"boost_number\",\n    trait_type: \"Aqua Power\",\n    value: Math.random() * 100,\n  },\n  {\n    display_type: \"boost_number\",\n    trait_type: \"Mana\",\n    value: Math.floor(Math.random() * 100),\n  },\n```\n\nYou are free to define _extra_ traits that you want each generated image to include in it's metadata, e.g., **health**.\n\n_Be sure to pass in a randomization function here, otherwise every json file will result in the same value passed here._\n\n## Optional\n\nThis also supports overwriting a trait normally assigned by the layer Name/folder and file name. If you'd like to overwrite it with some other value, adding the _same_ trait in `extraMetadata` will overwrite the default trait/value in the generated metadata.\n\n# Trait Value Overrides\n\n🧪 BETA FEATURE\n\nWhen you need to override the `trait_value` generated in the metadata.\n\nBy default trait values come from the file name _or_ subfolder that is _chosen_ (the last one in a nested structure with a weight delimiter).\nBecause many options require the filenames to be unique, there may be a situation where you need to overwrite the default value. To do this, set the overrides in `config.js`\n\n```js\n/**\n * In the event that a filename cannot be the trait value name, for example when\n * multiple items should have the same value, specify\n * clean-filename: trait-value override pairs. Wrap filenames with spaces in quotes.\n */\nconst traitValueOverrides = {\n  Helmet: \"Space Helmet\",\n  \"gold chain\": \"GOLDEN NECKLACE\",\n};\n```\n\n# Provenance Hash Generation\n\nIf you need to generate a provenance hash (and, yes, you should, [read about it here](https://medium.com/coinmonks/the-elegance-of-the-nft-provenance-hash-solution-823b39f99473) ), make sure the following in config.js is set to `true`\n\n```js\n//IF you need a provenance hash, turn this on\nconst hashImages = true;\n```\n\nThen…\nAfter generating images and data, each metadata file will include an `imageHash` property, which is a Keccak256 hash of the output image.\n\n## To generate the **Provenance Hash**\n\nrun the following util\n\n```\n  node utils/provenance.js\n```\n\n**The Provenance information is saved** to the build directory in `_prevenance.json`. This file contains the final hash as well as the (long) concatednated hash string.\n\n\\*Note, if you regenerate the images, **You will also need to regenerate this hash**.\n\n# Remove Trait Util\n\nIf you need to remove a trait from the generated `attributes` for ALL the generated metadata .json files, you can use the `removeTrait` util command.\n\n```\nnode utils/removeTrait.js \"Trait Name\"\n```\n\nIf you would like to print additional logging, use the `-d` flag\n\n```\nnode utils/removeTrait.js \"Background\" -d\n```\n\n# Randomly Insert Rare items - Replace Util\n\nIf you would like to manually add 'hand drawn' or unique versions into the pool of generated items, this utility takes a source folder (of your new artwork) and inserts it into the `build` directory, assigning them to random id's.\n\n## Requirements\n\n- create a source directory with an images and json folder (any name, you will specify later)\n- Name images sequentially from 1.png/jpeg (order does not matter) and place in the images folder.\n- Put matching, sequential json files in the json folder\n\nexample:\n\n```\n├── ultraRares\n│   ├── images\n│   │   ├── 1.png\n│   │   └── 2.png\n│   └── json\n│       ├── 1.json\n│       └── 2.json\n```\n\n**You must have matching json files for each of your images.**\n\n## Setting up the JSON.\n\nBecause this script randomizes which tokens to replace/place, _it is important_ to update the metadata properly with the resulting tokenId #.\n\n**_Everywhere_ you need the edition number in the metadata should use the `##` identifier.**\n\n```json\n  \"edition\": \"##\",\n```\n\n**Don't forget the image URI!**\n\n```json\n  \"name\": \"## super rare sunburn \",\n  \"image\": \"ipfs://NewUriToReplace/##.png\",\n  \"edition\": \"##\",\n```\n\n_if you need `#num` with the `#` in the name for example, use a different symbol other than `##`. see the --replacementSymbol flag below_\n\n## Running\n\nRun the script with the following command, passing in the source directory name, (relateive to the current working dir)\n\n```sh\nnode utils/replace.js [Source Directory]\n```\n\nexample\n\n```sh\nnode utils/replace.js ./ultraRares\n```\n\n## Flags\n\n### `--help`\n\nOutputs command help.\n\n### `--Debug`\n\n`-d` outputs additional logging information\n\n### `--replacementSymbol`\n\nIf you need the output data to have `#12` for example, with the leading #, the default `##` in the metadata is a problem. use this flag in combination with a different symbol in the metadata json files to replace the passed in symbol with the appropriate edition number\n\n```\nnode index.js ./ultraRares -r '@@'\n```\n\nThis will replace all instances of `@@` with the item number\n\n### `--identifier` \u003cidentifier\u003e\n\n`-i` Change the default object identifier/location for the edition/id number. defaults to \"edition\". This is used when the metadata object does not have \"edition\" in the top level, but may have it nested in \"properties\", for example, in which case you can use the following to locate the proper item in \\_metadata.json\n\n```\nnode utils/replace.js ./ultraRares -i properties.edition\n```\n\n⚠️ This step should be done BEFORE generating a provenance hash. Each new, replaced image generates a new hash and is inserted into the metadata used for generating the provenance hash.\n\n⚠️ This util requires the `build` directory to be complete (after generation)\n\n\u003chr /\u003e\n\n# Solana Metadata\n\n🧪 BETA FEATURE\n\nIf you are building for Solana, all the image generation options in config are available and are the same.\n\n## To setup your Solana specific metadata\n\nConfigure the `solona_config.js` file located in the `Solana/` folder.\nHere, enter in all the necessary information for your collection.\n\nYou can run the generator AND output Solana data by running the following command from your terminal\n\n```\nyarn generate:solana\n```\n\nIf you are using npm,\n\n```\nnpm run generate:solana\n```\n\n**After running, your Solana ready files will be in `build/solana`**\n\u003cbr/\u003e\n\u003cbr/\u003e\n\nIf you need to convert existing images/json to solana metadata standards, you can run the util by itself with,\n\n```\nnode utils/metaplex.js\n```\n\n# Cardano Metadata\n\n🧪 BETA FEATURE: Work in progress\n⚠️ Check the output metadata for cardano standards accuracy\n\nIf you are generating for Cardano, you can generate and output cardano formatted data at the same time, or run the util script separately after generation (or to an existing collection with proper data)\n\nFirst, edit the `cardano_config.js` file in the `Cardano/` folder with your information.\n\nThen, to generate images _and_ cardano data at once, run:\n\n```\nyarn generate:cardano\n```\n\nor if you're using npm\n\n```\nnpm run generate:cardano\n```\n\n## running the standalone cardano util\n\nIf you have an existing set of generated images and data, **and** you have a configured `Cardano/cardano_config.js` file, you can run the cardano conversion script with:\n\n```\nnode utils/cardano.js\n```\n\n# GIF compatibility\n\nThis tool currently does not support gif layers or outputting gifs. If you want to GIF generative tool, check out\n[Jalagar's GIF Engine](https://github.com/jalagar/Generative_Gif_Engine). It supports most of the same functionality\nas this repo (if-then, z-index, layering, Tezos, Solana, legendary replace). If you have any questions or issues,\nplease create a issue/discussion on Jalagar's repo.\n\n# incompatibilities\n\n⚠️ This was forked originally from hashlips 1.0.6 and may have different syntax/options. Be sure to read this readme for how to use each feature in _this_ branch.\n\n### Example:\n\nFor example, if you are using a `Backgrounds` layer and would prefer to remove that as a trait from the generated json,\nFirst, generate the images and json as usual, then, running the remove trait util\n\n```\n\nnode utils/removeTrait.js \"Background\"\n\n```\n\nWill remove the background trait from all metadata files.\n\n# Breaking Changes\n\n1. `extraMetadata` in prior version of this repo/branch, `extraMetadata` was used for attributes, it has been renamed `extraAttributes`\n\n\u003chr/\u003e\n\u003cbr/\u003e\n\u003chr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\n# Basic Setup\n\nThis is fork/combination of the original hashlips generator, for basic configuration\nCheck the [Basic Configuration readme](BASIC-README.md)\n\n```\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnftchef%2Fart-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnftchef%2Fart-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnftchef%2Fart-engine/lists"}