Skip to content

Instantly share code, notes, and snippets.

@tux-peng
Created April 11, 2026 01:29
Show Gist options
  • Select an option

  • Save tux-peng/0de8ab0467a30c1f353c7b7f55b0c1a2 to your computer and use it in GitHub Desktop.

Select an option

Save tux-peng/0de8ab0467a30c1f353c7b7f55b0c1a2 to your computer and use it in GitHub Desktop.
Create a customblocks.json for use with my crayola120.py and go server
import json
from PIL import Image
# The 120 Crayola colors mapped to their standard hex values
CRAYOLA_COLORS = {
# Reds & pinks
"Almond": "#EFDECD", "Apricot": "#FDD9B5", "Bittersweet": "#FD7C6E", "Blush": "#DE5D83",
"Brick Red": "#CB4154", "Carnation Pink": "#FFAACC", "Cerise": "#DD4492", "Chestnut": "#BC5D58",
"Jazzberry Jam": "#CA3767", "Mahogany": "#CD4A4C", "Mango Tango": "#FF8243", "Mauvelous": "#EF98AA",
"Melon": "#FDBCB4", "Peach": "#FFCBA4", "Pink Flamingo": "#FC74FD", "Pink Sherbert": "#F78FA7",
"Radical Red": "#FF496C", "Razzmatazz": "#E3256B", "Red": "#EE204D", "Salmon": "#FF9BAA",
"Scarlet": "#FC2847", "Sunset Orange": "#FD5E53", "Tickle Me Pink": "#FC89AC",
"Violet Red": "#F75394", "Wild Strawberry": "#FF43A4",
# Oranges
"Atomic Tangerine": "#FFA474", "Burnt Orange": "#FF7034", "Burnt Sienna": "#EA7E5D",
"Copper": "#DD9475", "Macaroni And Cheese": "#FFBD88", "Neon Carrot": "#FFA343",
"Orange": "#FF7538", "Outrageous Orange": "#FF6E4A", "Red Orange": "#FF5349",
"Tumbleweed": "#DEAA88", "Vivid Tangerine": "#FF9980",
# Yellows
"Banana Mania": "#FAE7B5", "Canary": "#FFFF99", "Dandelion": "#FED85D",
"Desert Sand": "#EFCDB8", "Gold": "#E7C697", "Goldenrod": "#FCD975",
"Laser Lemon": "#FEFE22", "Sunglow": "#FFCF48", "Tan": "#FAA76C",
"Unmellow Yellow": "#FFFF66", "Yellow": "#FCE883", "Yellow Orange": "#FFAE42",
# Yellow-greens & limes
"Electric Lime": "#CEFF1D", "Green Yellow": "#F0E891", "Inchworm": "#B2EC5D",
"Olive Green": "#BAB86C", "Spring Green": "#ECEABE", "Yellow Green": "#C5E384",
# Greens
"Asparagus": "#87A96B", "Fern": "#71BC78", "Forest Green": "#6DAE81",
"Granny Smith Apple": "#A8E4A0", "Green": "#1CAC78", "Jungle Green": "#3BB08F",
"Mountain Meadow": "#30BA8F", "Pine Green": "#158078", "Screamin Green": "#76FF7A",
"Sea Green": "#9FE2BF", "Shamrock": "#45CEA2", "Tropical Rain Forest": "#17806D",
# Blues & teals
"Aquamarine": "#78DBE2", "Blue": "#1F75FE", "Blue Green": "#0D98BA",
"Caribbean Green": "#1CD3A2", "Cerulean": "#1DACD6", "Cornflower": "#9ACEEB",
"Denim": "#2B6CC4", "Midnight Blue": "#1A4876", "Navy Blue": "#1974D2",
"Pacific Blue": "#1CA9C9", "Robin's Egg Blue": "#1FCECB", "Sky Blue": "#80DAEB",
"Timberwolf": "#DBD7D2", "Turquoise Blue": "#77DDE7",
# Purples & violets
"Blue Bell": "#A2A2D0", "Blue Violet": "#7366BD", "Cadet Blue": "#B0B7C6",
"Eggplant": "#6E5160", "Fuchsia": "#C364C5", "Indigo": "#5D76CB",
"Lavender": "#FCB4D5", "Manatee": "#979AAA", "Mulberry": "#C54B8C",
"Orchid": "#E6A8D7", "Outer Space": "#414A4C", "Periwinkle": "#C5D0E6",
"Plum": "#8E4585", "Purple Heart": "#7442C8", "Purple Mountain Majesty": "#9D81BA",
"Purple Pizzazz": "#FE4EDA", "Razzle Dazzle Rose": "#FF48D0", "Red Violet": "#C0448F",
"Royal Purple": "#7851A9", "Shocking Pink": "#FB7EFD", "Thistle": "#EBC7DF",
"Vivid Violet": "#8F509D", "Violet (Purple)": "#926EAE", "Wild Blue Yonder": "#A2ADD0",
"Wisteria": "#CDB4DB",
# Fluorescents
"Hot Magenta": "#FF1DCE", "Magenta": "#F664AF", "Wild Watermelon": "#FD5B78",
# Neutrals
"Antique Brass": "#CD9575", "Blizzard Blue": "#ACE5EE", "Maroon": "#C8385A",
"Sepia": "#A5694F", "Shadow": "#8A795D",
"Beaver": "#9F8170", "Black": "#000000", "Brown": "#B4674D",
"Fuzzy Wuzzy": "#CC6666", "Gray": "#95918C", "Silver": "#CDC5C2", "White": "#FFFFFF",
}
def is_slot_empty(img, tx, ty):
"""Checks if a 16x16 slot is empty (pure purple background in this terrain.png)."""
# EXPLICIT FIX: Protect the original Magenta Wool at Row 4, Column 11
if tx == 11 and ty == 4:
return False
# Look at the center pixel of the tile to determine if it's the empty purple background
r, g, b, a = img.getpixel((tx * 16 + 8, ty * 16 + 8))
if r > 200 and g < 150 and b > 200:
return True
return False
def main():
terrain = Image.open("terrain.png").convert("RGBA")
# 1. Build the list of names and their walking sounds (4=Stone, 7=Cloth)
block_definitions = []
for char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
block_definitions.append((f"Letter {char}", 4))
for char in "0123456789":
block_definitions.append((f"Number {char}", 4))
for color_name in CRAYOLA_COLORS.keys():
block_definitions.append((f"{color_name} Wool", 7))
blocks_json = []
# The ID to start counting from. Change this if you need them to start elsewhere!
current_id = 66
block_index = 0
total_new_blocks = len(block_definitions)
# 2. Scan the terrain sheet for empty slots
for ty in range(15):
for tx in range(16):
if block_index >= total_new_blocks:
break
if is_slot_empty(terrain, tx, ty):
# Calculate the exact texture ID for ClassicCube (Y * 16 + X)
tex_id = ty * 16 + tx
name, sound = block_definitions[block_index]
block_data = {
"id": current_id,
"name": name,
"solidity": 2, # 2 = Solid Block (Change to 0 if you want to walk through them)
"speed": 1,
"top_tex": tex_id,
"side_tex": tex_id,
"bottom_tex": tex_id,
"transmit_light": 0,
"walk_sound": sound,
"full_bright": 0,
"shape": 16, # 16 = Standard full block
"block_draw": 0, # 0 = Opaque (standard drawing)
"fog_density": 0,
"fog_r": 0,
"fog_g": 0,
"fog_b": 0,
"fallback": 0
}
blocks_json.append(block_data)
current_id += 1
block_index += 1
# 3. Write out the JSON file
with open("customblocks.json", "w") as f:
# indent=2 formats it exactly like the snippet you provided
f.write(json.dumps(blocks_json, indent=2))
print(f"Success! Generated customblocks.json with {len(blocks_json)} blocks.")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment