A New Generation of NFTs: Tubby Cats
September 13th, 2022

Tubby Cats is an art-based NFT project on Ethereum, created by @tubbystudios. As the developer responsible for image generation, I wanted to ensure the final collection would showcase each artists’ work and that every Tubby Cat would feel unique.

NFTs have become an increasingly popular way to showcase art and identity. Last year the industry saw an explosion of projects that were utilized as profile pictures on social media. This inspired the team to create a project where everyone would be able to find a Tubby Cat that they feel represents themselves.

This is why we put so much time & care into the Tubby Cats generation, generating over 150,000 images to review and refine before we launched our final collection of 20,000 Tubby Cats — so everyone could find their perfect pal.

There’s a Tubby Cat for everyone.

After our first handful of test gens, something seemed off. The individual traits were incredible and distinct, but when all of the assets were genned together, the composed images didn’t feel right.

At this time, I was generating Tubby Cats with a generic image generation algorithm, which is what a majority of NFT projects use. These algorithms typically use pre-determined rarities for each trait to compose images. Images generated from these algorithms can often look mismatched because of the complete randomness.

However, we wanted each Tubby Cat to feel unique. We wanted to make sure there was a Tubby Cat for everyone.

To achieve this, I created an image generation algorithm based on gen palettes.

Introducing Gen Palettes

A gen palette is a term I came up with to categorize traits and trait types based on colors and themes. Each individual trait was entered into one or multiple gen palettes. Traits would only be genned with other traits that were part of the same palette.

Below are Tubby Cats generated from each type of algorithm:

Generic algorithm
Generic algorithm
Palette-based algorithm
Palette-based algorithm

There’s a substantial difference in the above images. The images generated from gen palettes are aesthetic, cohesive, and most importantly: they appear to be created with intention.

The Tubby Cats gen palettes were either based on colors (e.g., Pink & Blue, Pastels, Monochrome) or theme (e.g., Fancy, Menhera, E-Girl). To create a gen palette, you would need to sort traits of each trait type into separate folders and then specify which folders should be genned for that palette.

Note: I do not think gen palettes are feasible for every NFT collection to utilize. Gen palettes worked well for Tubby Cats because of our unusually large number of traits (1600+) and our various trait types (17).

{
    "backgrounds": ["E-Girl"],
    "bodies": ["Ube", "Cotton Candy", "Mochi"],
    "tops": ["E-Girl"],
    "mouths": ["E-Girl"],
    "hats": ["E-Girl"],
    "clips": ["E-Girl"],
    "misc": ["E-Girl"],
    "hats_with_hair": ["E-Girl"],
    "sunglasses": ["E-Girl"],
    "eyewear": ["E-Girl"],
    "hair_colors": ["E-Girl"],
    "ear_piercings": ["Silver"],
    "hand_colors": ["E-Girl"],
    "eye_colors": ["Pink", "Purple", "Leak Eyes Pink", "Leak Eyes Purple"],
    "pets": [""],
    "costumes": [""],
    "whole_head_hats": [""]
  }

The above snippet is the JSON object used to generate our E-Girl palette. It specifies which folders it should pull from for all of the traits (Backgrounds, Bodies, Tops, etc.) and which trait types it should avoid (Pets, Costumes, and Whole Head Hats).

If you’re interested, you can view the final images generated from this palette on Opensea here.

Asset Sorting

The most time-consuming part of generating with palettes is asset sorting and organization. For each trait type, we created folders for each palette that used that trait type. Below demonstrates our palette-based organization for Tops.

Tops folder organization
Tops folder organization

If we open any of these folders, we can see the individual traits:

E-Girl Tops
E-Girl Tops

Rarity

Including single trait rarity while using gen palettes is difficult and can easily over-complicate the image generation process. To avoid over-complication, we set rarities based on palettes instead of by single traits. This is how we broke it down:

9 single color palettes: 15%
42 double color palettes: 50%
19 triple color palettes: 20%
41 themed palettes: 15%

This solved the issue of determining single trait rarity (very hard to do with 1600+ unique traits) and because the traits we wanted to remain rare were primarily in hyper-specific themed palettes, they were generated less-often.

Below is a list all the palettes we created and used in the final collection:

Palettes used in the Tubby Cats collection
Palettes used in the Tubby Cats collection

The Code

The algorithm starts by picking a random number between 1 and 100 to decide which type of palette will be used - single, double, triple, or themed. After the palette type is determined, the code will pick a random palette from that type.

# Random number to determine which palette type will be genned
random_palette_type_int = random.randint(0, 100)

if (random_palette_type_int <= 15): 
		# Use a single color palette
    rand_single_int = random.randint(0, len(single_palettes) - 1)
    basepaths = single_palettes[rand_single_int]
elif (random_palette_type_int <= 65):
		# Use a double color palette
    rand_double_int = random.randint(0, len(double_palettes) - 1)
    basepaths = double_palettes[rand_double_int]
elif (random_palette_type_int <= 85):
		# Use a triple color palette
    rand_triple_int = random.randint(0, len(triple_palettes) - 1)
    basepaths = triple_palettes[rand_triple_int]
else:
		# Use a themed "special" color palette
    rand_special_int = random.randint(0, len(special_palettes) - 1)
    basepaths = special_palettes[rand_special_int]

Once the specific palette has been determined, the code will grab all of the images & image names of every folder for each trait type.

# Get all files & filenames for Tops from paths specified in the theme's JSON object
def get_tops(path_names):
    top_images = []
    top_names = []
    for path_name in path_names:
        path = (f"./images/Tops Sorted/{path_name}")
        for f in os.listdir(path):
            valid_images = [".png", ".PNG", ".Png"]
            ext = os.path.splitext(f)[1]
            img_name = os.path.splitext(f)[0]
            if ext.lower() not in valid_images:
                continue
            top_names.append(img_ame)
            top_images.append(Image.open(os.path.join(path, f)))
    return [top_images, top_names]

In the main function of the image generation, we repeat this for all specified trait types of each palette. (Palettes are not required to use every trait type. For example, the E-Girl palette does not have Pets).

Once all of the individual traits have been selected for an image, we run a series of checks for traits that clip with one another. Depending on which traits the code selects, the image may need to be composed/layered differently. Once the image is composed, it’s saved along with the corresponding metadata.

Making Faces

From our test gens, we noticed some of our face traits didn’t fit well with each other (Mouth, Eyes, Eyebrows). We separated these assets into different categories so the faces made sense.

Eyebrows consisted of 3 categories: Regular, Sad, and Scared.

Eyes were sorted into 7 categories: Regular, Sad, Scared, Sleepy, Special, Colored, and Other - which included unique eye types like Dizzy, Leak, and MultiEyes.

Mouths were sorted into 5 categories: Regular, Sad, Scared, Special, and Accessory - which included theme-based mouths.

Putting It All Together

Below are a few scenarios that could be produced from the algorithm. Trait types are chosen randomly. As you’ll see below certain traits are not required.

Step-by-step image generation process
Step-by-step image generation process

Randomness ensures each Tubby Cat is unique - not only by palette, but by amount and types of traits as well.

Below are examples of images generated from various themes in the final collection:

Palette-based generation output
Palette-based generation output

Reviewing & Finalizing the Collection

How do you efficiently review 20,000 images and their corresponding metadata?

We created an internal review tool that allowed the team to review the Tubby Cats one-by-one to check for any issues such as clips (conflicting traits) and incorrect metadata.

It took the team about 2 weeks to review all of the images and replace where necessary. Tubby Cats that needed replacement were replaced with hand-made Tubby Cats and one-of-ones.

To ensure certain traits were included together and the highest quality Tubby Cats were included in the collection, we created another internal website where our team could “dress-up” Tubby Cats.

These hand-made Tubby Cats would be sent to a Discord channel with their metadata which would be used to up-res the images. In total, roughly 2000 hand-made Tubby Cats are in the final collection.

Generation Token

An NFT to compliment this article has been minted on ZORA and is available to mint for 0.042 ETH with 420 editions available. Minting is available here.

Complimentary NFT
Complimentary NFT

Art is by @sugoiNFT.

A New Generation of NFTs

I hope Tubby Cats inspires NFT creators to utilize gen palettes. Generating images based on palettes makes a huge difference and allows artists’ work to be better appreciated.

There’s a Tubby Cat for everyone. What’s yours?

If you have questions about implementing gen palettes or just want to chat, you can find me on Twitter.

Subscribe to guccitroll
Receive the latest updates directly to your inbox.
Nft graphic
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from guccitroll

Skeleton

Skeleton

Skeleton