2Lovelaces
Rewarding Completed Catalyst Proposals: How Lido Nation Generates NFT Badges with Python

Rewarding Completed Catalyst Proposals: How Lido Nation Generates NFT Badges with Python

Par Willard Owiti

Imagine thousands of ambitious projects submitting ideas to Cardano Catalyst, each with dreams of transforming the ecosystem. Some succeed. When they do, we think they deserve more than just a checkmark in a database—they deserve recognition.

At Lido Nation, we built a system to honor completed Catalyst proposals with commemorative NFT badges. These badges aren’t just digital bragging rights; they’re on-chain proof of achievement, tied to a specific proposal and timestamped forever on the blockchain. When a project reaches completion, we automatically generate a unique, visually striking badge that celebrates the team’s work and links directly to their proposal page.

See an example of a completed proposal badge here.

The creative vision for these badges came from our co-founder Stephanie Phuffy, who designed the layering structure and visual approach. Her insight was key: instead of creating static badge designs, we could compose them dynamically by layering different visual elements with blend modes—allowing for infinite variation while maintaining brand consistency.

The challenge wasn’t just about minting NFTs. We needed to produce hundreds of distinctive, on-brand badges reliably and at scale—without pre-rendering endless variations or storing massive asset libraries. Stephanie’s layer design made this possible. The solution: a clever Python script that layers multiple images with sophisticated blend modes to create the final artwork on-demand.

This post walks through imageLayering2.py—the script that brings Stephanie’s vision to life.

The Problem

For proposal-completion badges, we needed:

  • Brand consistency across funds and categories
  • Proposal-specific visuals (logos, icons, textures)
  • Depth and lighting that feels celebratory, not flat
  • Deterministic output for the same inputs, plus batch performance

The Solution: Multi-Stage Image Blending

We combine five layers with three blend modes to produce a rich, celebratory badge for each completed proposal.

import traceback
from PIL import Image
import numpy as np
import sys, os, uuid
from blend_modes import lighten_only, soft_light, screen

We’re using:

  • PIL (Pillow) for image I/O and conversion
  • NumPy for efficient pixel-level operations
  • blend_modes library for advanced blending algorithms
  • UUID to generate unique filenames

The Architecture

The function accepts 6 parameters representing 5 image files:

def process_images(background_path, foreground_path_1, foreground_path_2, overlay_path_1, overlay_path_2, output_path):

  • Background — Fund/theme backdrop for the proposal’s funding round
  • Foreground 1 — Primary badge frame and core motif
  • Foreground 2 — Project-specific texture/logo or category ornament
  • Overlay 1 — “Completed” seal/date or highlight accents
  • Overlay 2 — Lighting/glow and subtle round watermark
  • Output — Directory to save the rendered badge

Step 1: Loading and Format Conversion

background = Image.open(background_path).convert("RGBA")
foreground_1 = Image.open(foreground_path_1).convert("RGBA")
foreground_2 = Image.open(foreground_path_2).convert("RGBA")
overlay = Image.open(overlay_path_1).convert("RGBA")
overlay_2 = Image.open(overlay_path_2).convert("RGBA")

Converting to RGBA is crucial—it ensures every image has an alpha channel for transparency support.

Step 2: Resizing and Canvas Setup

width, height = foreground_1.size
overlay = overlay.resize((width, height))
overlay_2 = overlay_2.resize((width, height))

transparent_img = Image.new("RGBA", (width, height), (0, 0, 0, 0))

All overlays are resized to match the foreground dimensions. The transparent canvas serves as our composition base.

Step 3: The Dual Background Technique

transparent_img.paste(background, (0, 0))

vertical_offset = 145
horizontal_offset = -4
bg_width, bg_height = background.size
bg_position = (
((width - bg_width) // 2) + horizontal_offset,
((height - bg_height) // 2) - vertical_offset
)

transparent_img.paste(background, bg_position, background)
transparent_img.paste(foreground_1, (0, 0), foreground_1)

Here’s the clever part: the background is pasted twice:

  1. First at full coverage to fill the canvas
  2. Second with a 145px vertical offset and 4px horizontal shift

This creates depth and shadow effects without additional processing. The foreground is then placed on top, serving as the main subject.

Step 4: The Three-Stage Blend

This is where the magic happens. We convert images to float arrays for pixel-level precision:

background_float = np.array(transparent_img).astype(float)
foreground_2_float = np.array(foreground_2).astype(float)
overlay_float = np.array(overlay).astype(float)
overlay_2_float = np.array(overlay_2).astype(float)

# Stage 1: Soft Light
blended_soft_light = soft_light(background_float, foreground_2_float, 1.0)

# Stage 2: Lighten Only
blended_lighten = lighten_only(blended_soft_light, overlay_float, 1.0)

# Stage 3: Screen
final_blend = screen(blended_lighten, overlay_2_float, 1.0)

Understanding Each Blend Mode

Soft Light (Stage 1: Project texture meets badge frame)

  • Applies subtle lighting effects from the project layer
  • Darkens where the project texture is dark, brightens where it’s light
  • Creates depth without overpowering the badge frame
  • Perfect for layering project-specific visuals without washing out the design

Lighten Only (Stage 2: Completion seal overlay)

  • Compares each pixel and keeps the brighter value
  • Ideal for adding a “Completed” badge or timestamp without losing visibility
  • Creates glowing accent details (stars, checkmarks, date badges)
  • Only adds brightness, never darkens

Screen (Stage 3: Final glow and highlight)

  • The inverse of multiply mode
  • Brightens the image significantly to create a celebratory glow
  • Creates that ethereal, achievement-like quality
  • Commonly used for light rays, halos, and final polish

Step 5: Output and Uniqueness

final_image = Image.fromarray(np.uint8(final_blend))
filename = str(uuid.uuid4()) + ".png"

if not os.path.exists(output_path):
os.makedirs(output_path)

output_file_path = os.path.join(output_path, filename)
final_image.save(output_file_path, format="PNG")

return filename

Each generated image gets a UUID filename, ensuring uniqueness and preventing collisions in batch operations. Once generated, the badge artwork is minted as an NFT on Cardano and indexed on block explorers like pool.pm, where it becomes permanently part of the blockchain.

How Lido Nation Uses This

At Lido Nation, this workflow enables us to:

  • Track Catalyst Progress - Automatically generate badges when proposals transition to “completed” status
  • Fund/Category Branding - Swap fund-specific backgrounds and frames based on which Catalyst round the proposal belongs to
  • Batch Processing - Generate hundreds of completion badges in parallel without blocking the API
  • Archivable Proof - Each badge is a timestamped NFT tied to a specific proposal, creating permanent proof of project completion on-chain

Proposals in Cardano Catalyst are ambitious undertakings. When teams complete their work, we celebrate it with a commemorative badge that lives on their proposal page and in the funder’s wallet.

Error Handling

except Exception as e:
return f"Error: {traceback.format_exc()}"

The function gracefully returns error details instead of crashing, making it safe for batch operations.

Command-Line Usage

if __name__ == "__main__":
background_path = sys.argv[1]
foreground_path_1 = sys.argv[2]
foreground_path_2 = sys.argv[3]
overlay_path_1 = sys.argv[4]
overlay_path_2 = sys.argv[5]
output_path = sys.argv[6]

filename = process_images(...)
print(filename)

This can be called from any system—Node.js, Python, or shell scripts—making it highly integrable.

Performance Considerations

For production use with thousands of images:

  • Batch processing - Queue multiple jobs
  • Async execution - Don’t block the main thread
  • Caching - Store frequently used layers in memory
  • Optimization - Consider image compression before blending

Conclusion

At Lido Nation, we’ve turned a technical challenge into a creative advantage. By combining Stephanie’s layer design with sophisticated image blending, we’re able to produce professional, on-brand NFT badges that celebrate the completion of Cardano Catalyst proposals at scale.

If you’re building systems that need to generate unique, branded artwork programmatically—whether for achievements, credentials, or commemorations—this multi-stage blending approach offers a flexible, performant foundation.

The code is straightforward, but the results are striking. And that’s the beauty of thoughtful image composition.

Commentaires

Soyez le premier à partager vos pensées !

Laisser un commentaire