Synth(etic) Punks - (Free Unlimited) 24×24 Pixel Punks For Everyone
synthpunks - (free unlimited) 24×24 pixel punks for everyone - yes, you can - generate / claim your own synth(etic) punks ("off-blockchain") for your own (ethereum) account 40-hexdigit addresses e.g. 0x054f3b6eadc9631ccd60246054fdb0fcfe99b322; incl. 2x/4x/8x zoom for bigger sizes and more
Background - What are "On-Chain" Synthetic (Crypto) Punks V1?
Fully on-chain (crypto) punks for every ethereum address. Check yours out for free!
-- syntheticpunks.com
Stephan Cilliers (from South Africa)
has put together an open-source blockchain service ( - see stephancill/synthetic-punks - )
to generate for free¹ 24×24 punk pixel heads for every ethereum address (account).
¹: and optionally claim / mint a (non-fungible) token for a 0.02 ether fee.
The F.A.Q. reads:
Q: What are Synthetic Punks?
A: Synthetic Punks is inspired by the historical
collection of 10 000 Matt & John's® Punks [Anno 2017]
by Larva Labs and Synthetic Loot by Dom Hofmann.
It generates a unique, fully on-blockchain punk for each Ethereum address.
They are free to view for any address, but can be claimed [minted]
as an ERC-721 non-fungible token (NFT)
for a price of 0.02 ether.
Features - Each Synthetic Punk
- Is generated from assets stored fully on-blockchain
- Is uniquely associated with an ethereum wallet address
- Supports Ethereum Name Service (ENS) in its metadata
"Off-Chain" Usage In Your Script
Anyways, the synthpunks gem / library is inspired by Synthetic Punks V1
by Stephan Cilliers (see above)
and lets you generate punks using the "original"
Synthetic Punks V1 pseudo-random number and image generation formula
"off-blockchain"¹
using the "original" Synthetic Punks V1 spritesheet
and attribute categories (ranges) and names.
Let's try
the (ethereum) address 0x054f3b6eadc9631ccd60246054fdb0fcfe99b322:
require 'synthpunks'
punk = Synthpunks::Image.generate('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
punk.save( 'punk1.png' )
punk.zoom(8).save( 'punk1@8x.png' )
resulting in:
8x
Tip: Use Synthpunks.getTokenID( addr )
to get the "official" token id
from the address (that really is only a "convenience" helper that converts the 40-digit / 20-byte hexstring to a decimal (big) integer number). Example:
Synthpunks.getTokenID('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
'0x054f3b6eadc9631ccd60246054fdb0fcfe99b322'.to_i( 16 )
And use the new exclusive "off-chain" only Synthpunks.getAssetNames( id )
helper
to get all (random) punk attributes by name.
Synthpunks.getAttributeNames( 30311890011735557186986086868537068337617285922 )
Now look up the minted
Synthetic (Crypto) Punks V1 on the open sea (secondary)
market:
Minted Synthetic Punk for 0x054f3b6eadc9631ccd60246054fdb0fcfe99b322
with Token ID #30311890011735557186986086868537068337617285922 @ Open Sea
Or see
Synthetic Punk for 0x054f3b6eadc9631ccd60246054fdb0fcfe99b322
And spot the differencence - hint: none other
than the "dark / black-ish" background is missing.
Let's retry and let's add the "dark / black-ish" background
that is, #1A1A1A, using the rgb hex color code:
punk = Synthpunks::Image.generate('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
punk = punk.background( '#1A1A1A' )
punk.save( 'punk1b.png' )
punk.zoom(8).save( 'punk1b@8x.png' )
resulting in:
8x
Or for fun let's try some more new "custom" backgrounds.
Example: Stand with Ukraine / Ukraine Flag:
punk = Synthpunks::Image.generate('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
punk = punk.background( 'ukraine' )
punk.save( 'punk1c.png' )
punk.zoom(8).save( 'punk1c@8x.png' )
resulting in:
8x
or
punk = punk.background( 'rainbow' )
resulting in:
8x
and so on.
Tip: Looking for left-looking phunks? Try Image#mirror
.
Example:
punk = Synthpunks::Image.generate('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
phunk = punk.mirror
phunk.save( 'phunk1.png' )
phunk.zoom(8).save( 'phunk1@8x.png' )
resulting in:
8x
Bonus Tip: Derive your own custom default profile pictures
using Image#silhouette
. Example:
punk = Synthpunks::Image.generate('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
punk = punk.silhouette( '0x647785' ).background( '0xCCD5DE' )
punk.save( 'punk1-default1.png' )
punk.zoom(8).save( 'punk1-default1@8x.png' )
resulting in:
8x
or is blue the new gray?
punk = punk.silhouette( '0x4474E0' ).background( '0xA0C2FF' )
resulting in:
8x
and so on.
Yes, you can.
Let's try some more
(ethereum) addresses.
punk = Synthpunks::Image.generate( '0x7a80ee32044f496a7bfef65af738fdda3a02cf02' )
punk.save( "punk2.png" )
punk.zoom(8).save( "punk2@8x.png" )
resulting in:
8x
and the attributes
tokenID = Synthpunks.getTokenID( '0x7a80ee32044f496a7bfef65af738fdda3a02cf02' )
Synthpunks.getAttributeNames( tokenID )
and compare with the "original" with the attributes:
- Human 1
- Rosy Cheeks
- Wild Hair
- VR
- Shadow Beard
Minted Synthetic Punk for 0x7a80Ee32044F496A7bFeF65Af738FddA3a02CF02
with Token ID #699372119169819039191610289391678040975564001026 @ Open Sea
Or see Synthetic Punk for 0x7a80Ee32044F496A7bFeF65Af738FddA3a02CF02
and so on.
¹: Yes, the (on-chain) solidity contract code
got translated "by-hand" to (off-chain) ruby script.
Example - the entropy pseudo-random number generator source:
function randomUint(uint256 seed, uint256 offset) public view returns (uint256)
{
require(offset < 32, "Offset out of bounds");
bytes32 entropy = keccak256(abi.encodePacked(address(this), seed));
bytes32 mask = bytes32(0xff << (offset * 8));
uint256 out = uint256((entropy & mask) >> (offset * 8));
return out;
}
and the matching ruby version:
ADDRESS = '0xaf9CE4B327A3b690ABEA6F78eCCBfeFFfbEa9FDf'
def randomUint( seed, offset )
entropy = keccak256( hex_to_bin( ADDRESS ) +
hex_to_bin( uint256_to_hex(seed)) )
bytes = entropy.bytes.reverse
bytes[offset]
end
License
The scripts are dedicated to the public domain.
Use it as you please with no restrictions whatsoever.
Post them on the D.I.Y. Punk (Pixel) Art reddit. Thanks.