๐Ÿธ My First Minecraft Mod ยท Episode 5 of 6 ยท See All Episodes
๐Ÿธ Episode 5 ยท Custom Mob ยท Crystal Frog

Crystal Frog!
Custom Mob

Bring your mod to life by adding a brand new animal the Crystal Frog! It wanders around, follows players holding Crystal Gems, and drops loot when defeated.

๐Ÿง‘ Ages 10+ โฑ๏ธ ~2 Hours ๐Ÿธ Fabric 1.21 โœ“ Free
Your Progress 0 / 11 Steps
1
๐Ÿงฉ
How Mobs Work in Fabric
Learn what files you need before writing a single line of code
In Progress
๐Ÿธ
Goal โ€” understand the mob recipe

A custom mob needs: an Entity class (the brain), an EntityType registration, a Renderer (how it looks), a Renderer registration, a model (the shape), a texture (the skin), a spawn egg item, and a loot table. That's a lot but we'll build each piece together!

Files we'll create this episode

  • 1CrystalFrogEntity.java โ€” the frog's brain, health, AI goals
  • 2ModEntities.java โ€” registers the entity type with Fabric
  • 3CrystalFrogModel.java โ€” the 3D box model
  • 4CrystalFrogRenderer.java โ€” tells Minecraft to draw the frog
  • 5ModEntityRenderers.java โ€” client-side renderer registration
  • 6crystal_frog.png โ€” the texture / skin
  • 7Loot table JSON โ€” what drops when the frog dies
  • 8Spawn egg item โ€” added to ModItems.java
โ„น๏ธ Client vs Server? In Minecraft, game logic (health, AI, drops) runs on the server side. Rendering (drawing the model on screen) runs on the client side. That's why we need two registration classes one for each side.
๐Ÿ’ก We're extending AnimalEntity โ€” the same base class as pigs, cows, and frogs. This gives us wandering behaviour, breeding, sounds, and health for free!
I understand there are several files to create for a mob
I know the difference between server-side logic and client-side rendering
2
๐Ÿง 
Create CrystalFrogEntity.java
Write the frog's brain health, sound, and AI goals
Locked
๐Ÿง 
Goal โ€” give the frog a brain

Create CrystalFrogEntity.java in your crystalmod package. This class holds health, sounds, and AI goals that make the frog wander and follow players holding Crystal Gems.

Create the file

Right-click the com.example.crystalmod package โ†’ New โ†’ Java Class โ†’ name it CrystalFrogEntity

CrystalFrogEntity.java
package com.example.crystalmod; import net.minecraft.entity.*; import net.minecraft.entity.ai.goal.*; import net.minecraft.entity.attribute.*; import net.minecraft.entity.passive.AnimalEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.Items; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundEvent; import net.minecraft.sound.SoundEvents; import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; public class CrystalFrogEntity extends AnimalEntity { public CrystalFrogEntity(EntityType<? extends AnimalEntity> entityType, World world) { super(entityType, world); } // Give the frog its base stats public static DefaultAttributeContainer.Builder createCrystalFrogAttributes() { return AnimalEntity.createAnimalAttributes() .add(EntityAttributes.MAX_HEALTH, 10.0) // 5 hearts .add(EntityAttributes.MOVEMENT_SPEED, 0.25); // slightly fast } // Register AI goals โ€” what the frog does @Override protected void initGoals() { this.goalSelector.add(1, new SwimGoal(this)); this.goalSelector.add(2, new TemptGoal(this, 1.1, ModItems.CRYSTAL_GEM, false)); this.goalSelector.add(3, new WanderAroundFarGoal(this, 0.8)); this.goalSelector.add(4, new LookAtEntityGoal(this, PlayerEntity.class, 6.0f)); this.goalSelector.add(5, new LookAroundGoal(this)); } // Required by AnimalEntity frogs can't breed in this mod @Override public @Nullable PassiveEntity createChild(ServerWorld world, PassiveEntity entity) { return null; } // Sound effects @Override protected SoundEvent getAmbientSound() { return SoundEvents.ENTITY_FROG_AMBIENT; } @Override protected SoundEvent getHurtSound(DamageSource source) { return SoundEvents.ENTITY_FROG_HURT; } @Override protected SoundEvent getDeathSound() { return SoundEvents.ENTITY_FROG_DEATH; } }
โ„น๏ธ AI Goal priority numbers lower numbers win. So swimming (1) beats following gems (2), which beats wandering (3). The frog won't wander into the sea chasing you!
๐Ÿ’ก TemptGoal makes the frog follow any player holding an item. We passed ModItems.CRYSTAL_GEM so hold a Crystal Gem and the frog will come running!
Created CrystalFrogEntity.java in the crystalmod package
No red squiggles (IntelliJ can find all the imports)
3
๐Ÿ“‹
Create ModEntities.java
Register the entity type so Minecraft knows it exists
Locked
๐Ÿ“‹
Goal โ€” register the Crystal Frog entity type

Just like items and blocks, entities need to be registered. Create ModEntities.java to give the Crystal Frog an EntityType which includes its bounding box size and spawn behaviour.

ModEntities.java
package com.example.crystalmod; import net.minecraft.entity.*; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; public class ModEntities { public static final EntityType<CrystalFrogEntity> CRYSTAL_FROG = Registry.register( Registries.ENTITY_TYPE, Identifier.of(CrystalMod.MOD_ID, "crystal_frog"), EntityType.Builder .create(CrystalFrogEntity::new, SpawnGroup.CREATURE) .dimensions(0.6f, 0.6f) // width, height in blocks .build() ); public static void registerModEntities() { CrystalMod.LOGGER.info("Registering mod entities for " + CrystalMod.MOD_ID); } }
โ„น๏ธ SpawnGroup.CREATURE means it spawns naturally like animals (pigs, cows). The alternative is MONSTER for hostile mobs. Our frog is friendly so CREATURE is correct.
โš ๏ธ Now open CrystalMod.java and add ModEntities.registerModEntities(); inside onInitialize(). Don't forget this or the entity won't be registered!

Update CrystalMod.java

CrystalMod.java โ€” onInitialize()
@Override public void onInitialize() { ModEntities.registerModEntities(); // <-- add this line ModBlocks.registerModBlocks(); ModItems.registerModItems(); CrystalMod.LOGGER.info("Crystal Mod loaded! ๐Ÿ’Ž"); }
Created ModEntities.java
Added ModEntities.registerModEntities() to CrystalMod.java
4
๐ŸŽจ
Create the Frog Model
Build the 3D box model that defines the frog's shape
Locked
๐ŸŽจ
Goal give the frog a body shape

The model defines how the frog looks which parts it has, their sizes and positions. We'll keep it simple: one body cube and four leg stubs. You can always make it fancier later!

Create the model file

Right-click the crystalmod package โ†’ New โ†’ Java Class โ†’ CrystalFrogModel

CrystalFrogModel.java
package com.example.crystalmod; import net.minecraft.client.model.*; import net.minecraft.client.render.entity.model.SinglePartEntityModel; public class CrystalFrogModel<S extends LivingEntityRenderState> extends SinglePartEntityModel<S> { private final ModelPart root; public CrystalFrogModel(ModelPart root) { this.root = root; } public static TexturedModelData getTexturedModelData() { ModelData modelData = new ModelData(); ModelPartData modelPartData = modelData.getRoot(); // Body a wide flat cube modelPartData.addChild("body", ModelPartBuilder.create().uv(0, 0).cuboid(-4f, -2f, -3f, 8, 3, 6), ModelTransform.pivot(0, 21, 0) ); // Front left leg modelPartData.addChild("leg_fl", ModelPartBuilder.create().uv(0, 9).cuboid(-1f, 0f, -1f, 2, 3, 2), ModelTransform.pivot(-3, 21, -3) ); // Front right leg modelPartData.addChild("leg_fr", ModelPartBuilder.create().uv(8, 9).cuboid(-1f, 0f, -1f, 2, 3, 2), ModelTransform.pivot(3, 21, -3) ); // Back left leg modelPartData.addChild("leg_bl", ModelPartBuilder.create().uv(0, 9).cuboid(-1f, 0f, -1f, 2, 3, 2), ModelTransform.pivot(-3, 21, 3) ); // Back right leg modelPartData.addChild("leg_br", ModelPartBuilder.create().uv(8, 9).cuboid(-1f, 0f, -1f, 2, 3, 2), ModelTransform.pivot(3, 21, 3) ); return TexturedModelData.of(modelData, 32, 32); // texture is 32x32 pixels } @Override public ModelPart getPart() { return root; } @Override public void setAngles(S state) { super.setAngles(state); // Basic leg swing animation using render state limbFrequency float swing = state.limbFrequency; this.root.getChild("leg_fl").pitch = swing; this.root.getChild("leg_br").pitch = swing; this.root.getChild("leg_fr").pitch = -swing; this.root.getChild("leg_bl").pitch = -swing; } }
โ„น๏ธ cuboid(x, y, z, width, height, depth) the first three numbers are the offset from the pivot point, the last three are the size. The pivot is where the part rotates from (e.g. the hip joint for a leg).
โœจ Want a fancier model? Try Blockbench a free 3D editor made for Minecraft mobs. You can design your frog visually and export the Java model code!
Created CrystalFrogModel.java with body and 4 legs
No red squiggles in IntelliJ
5
๐Ÿ–ผ๏ธ
Create Renderer & Register It
Tell Minecraft how to draw the Crystal Frog on screen
Locked
๐Ÿ–ผ๏ธ
Goal โ€” connect the model to the texture

Create CrystalFrogRenderer.java to pair the model with its texture file. Then create ModEntityRenderers.java to register the renderer on the client side. These run only when the game is rendering (client), not during gameplay logic (server).

CrystalFrogRenderer.java

CrystalFrogRenderer.java
package com.example.crystalmod; import net.minecraft.client.render.entity.*; import net.minecraft.client.render.entity.state.LivingEntityRenderState; import net.minecraft.util.Identifier; public class CrystalFrogRenderer extends MobEntityRenderer<CrystalFrogEntity, LivingEntityRenderState, CrystalFrogModel<LivingEntityRenderState>> { private static final Identifier TEXTURE = Identifier.of(CrystalMod.MOD_ID, "textures/entity/crystal_frog.png"); public CrystalFrogRenderer(EntityRendererFactory.Context context) { super(context, new CrystalFrogModel<>( CrystalFrogModel.getTexturedModelData().createModel() ), 0.4f // shadow radius ); } @Override public LivingEntityRenderState createRenderState() { return new LivingEntityRenderState(); } @Override public void updateRenderState(CrystalFrogEntity entity, LivingEntityRenderState state, float tickDelta) { super.updateRenderState(entity, state, tickDelta); } @Override public Identifier getTexture(LivingEntityRenderState state) { return TEXTURE; } }

ModEntityRenderers.java

ModEntityRenderers.java
package com.example.crystalmod; import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; public class ModEntityRenderers { public static void registerEntityRenderers() { EntityRendererRegistry.register( ModEntities.CRYSTAL_FROG, CrystalFrogRenderer::new ); } }
โš ๏ธ Client-side registration goes in a different class. Create a new file called CrystalModClient.java (next step) that's where we call registerEntityRenderers(). Never call it from CrystalMod.java or the game will crash on dedicated servers!
Created CrystalFrogRenderer.java
Created ModEntityRenderers.java
6
๐Ÿ’ป
Create CrystalModClient.java
The client-side entry point โ€” where rendering setup lives
Locked
๐Ÿ’ป
Goal โ€” set up the client entry point

Fabric has separate entry points for server-side code and client-side code. Rendering only runs on the client. We need to create CrystalModClient.java and register it in fabric.mod.json.

CrystalModClient.java

CrystalModClient.java
package com.example.crystalmod; import net.fabricmc.api.ClientModInitializer; public class CrystalModClient implements ClientModInitializer { @Override public void onInitializeClient() { ModEntityRenderers.registerEntityRenderers(); } }

Update fabric.mod.json

Open src/main/resources/fabric.mod.json and add a client entry point:

fabric.mod.json update entrypoints section
{ "schemaVersion": 1, "id": "crystalmod", "version": "1.0.0", "name": "Crystal Mod", "entrypoints": { "main": ["com.example.crystalmod.CrystalMod"], "client": ["com.example.crystalmod.CrystalModClient"] } }
๐Ÿ’ก The main entry point runs on both client and server. The client entry point runs only when a player is in-game with graphics. This separation prevents crashes on headless dedicated servers.
Created CrystalModClient.java implementing ClientModInitializer
Added "client" entry point to fabric.mod.json
๐ŸŽ“ Teacher Note Step 6This is the most common crash in Episode 5. Rendering code must never run on a dedicated server (which has no display). The client entry point is Fabric's safety mechanism. If a student's game crashes with a NullPointerException in rendering, 80% chance they put renderer registration in the wrong entry point.
๐Ÿšจ Stuck? Game crashes or frog is invisible
  • Frog is invisible (no texture) most likely the client entry point wasn't registered. Double-check fabric.mod.json has a "client" key pointing to com.example.crystalmod.CrystalModClient.
  • ClassNotFoundException: CrystalModClient โ€” the class name in fabric.mod.json must exactly match the Java class name, including capitalisation.
  • CrystalModClient won't compile it must implement ClientModInitializer, not ModInitializer. Import: net.fabricmc.api.ClientModInitializer.
  • EntityRendererRegistry not found Alt+Enter โ†’ Import โ†’ net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry.
7
๐Ÿ–ผ๏ธ
Create the Frog Texture
Paint a 32ร—32 pixel skin for your Crystal Frog
Locked
๐ŸŽจ
Goal create the texture file

Every mob needs a texture (its skin). We'll create a 32ร—32 PNG file. We defined the texture size as 32ร—32 in CrystalFrogModel the UV coordinates map parts of the model to areas of this image.

Create the folder structure

Folder path
src/main/resources/assets/crystalmod/textures/entity/crystal_frog.png
  • 1In your file explorer, navigate to src/main/resources/assets/crystalmod/textures/
  • 2Create a new folder called entity inside the textures folder
  • 3Open any pixel art editor (even MS Paint works!) and create a 32ร—32 pixel image
  • 4Paint it! Use bright crystal blues and teals โ€” remember the body UVs start at (0,0) and legs start at (0,9) and (8,9)
  • 5Save it as crystal_frog.png inside the entity folder
๐ŸŽจ Quick start tip: Make the whole image a solid cyan/teal colour first. That will make your frog appear as a glowing blue blob which already looks cool! Then you can add details later.
๐Ÿ’ก Free pixel art tools: Piskel (online, free) or LibreSprite (desktop, free). Both are great for making Minecraft textures!
Created the entity folder inside textures
Created crystal_frog.png (32ร—32 pixels)
8
๐Ÿฅš
Add the Spawn Egg
Create a spawn egg item so you can summon frogs in creative mode
Locked
๐Ÿฅš
Goal โ€” add a Crystal Frog Spawn Egg to creative mode

Fabric provides a SpawnEggItem class that automatically handles right-click-to-spawn. We just need to register it in ModItems.java and give it two colours (the egg body and the spots).

Add spawn egg to ModItems.java

Open ModItems.java and add these two new lines:

ModItems.java add spawn egg
// Add this import at the top of the file import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry; import net.minecraft.item.SpawnEggItem; // Add this field after CRYSTAL_SWORD public static final Item CRYSTAL_FROG_SPAWN_EGG = registerItem("crystal_frog_spawn_egg", new SpawnEggItem(ModEntities.CRYSTAL_FROG, 0x00bcd4, // egg body colour cyan 0x7fffd4, // spot colour aquamarine new Item.Settings() ) );

Register entity attributes

This must happen in CrystalMod.java's onInitialize it tells Minecraft what stats the Crystal Frog has:

CrystalMod.java add attribute registration
// Add this import import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry; // Add this inside onInitialize(), before registerModItems() FabricDefaultAttributeRegistry.register( ModEntities.CRYSTAL_FROG, CrystalFrogEntity.createCrystalFrogAttributes() );

Add spawn egg to creative tab

Still in ModItems.java, add the spawn egg to the creative inventory inside registerModItems():

ModItems.java registerModItems()
public static void registerModItems() { ItemGroupEvents.modifyEntriesEvent(ItemGroups.INGREDIENTS).register(entries -> { entries.add(CRYSTAL_GEM); entries.add(MAGIC_COOKIE); entries.add(CRYSTAL_PICKAXE); entries.add(CRYSTAL_SWORD); entries.add(CRYSTAL_FROG_SPAWN_EGG); // <-- new! }); }

Add spawn egg model JSON

assets/crystalmod/models/item/crystal_frog_spawn_egg.json
{ "parent": "minecraft:item/template_spawn_egg" }

Add language entry

en_us.json โ€” add this line
"item.crystalmod.crystal_frog_spawn_egg": "Crystal Frog Spawn Egg"
Added CRYSTAL_FROG_SPAWN_EGG to ModItems.java
Registered entity attributes in CrystalMod.java
Created spawn egg model JSON file
Added language entry to en_us.json
9
๐Ÿ’€
Add a Loot Table
Make the frog drop Crystal Gems when defeated
Locked
๐Ÿ’Ž
Goal โ€” reward players who defeat a Crystal Frog

Create a loot table JSON that makes Crystal Frogs drop 1-3 Crystal Gems when killed. Entity loot tables go in a different folder from block loot tables.

Create the loot table

Folder path
src/main/resources/data/crystalmod/loot_table/entities/crystal_frog.json
crystal_frog.json
{ "type": "minecraft:entity", "pools": [ { "bonus_rolls": 0.0, "entries": [ { "type": "minecraft:item", "name": "crystalmod:crystal_gem", "functions": [ { "function": "minecraft:set_count", "count": { "type": "minecraft:uniform", "min": 1, "max": 3 } }, { "function": "minecraft:looting_enchant", "count": { "type": "minecraft:uniform", "min": 0, "max": 1 } } ] } ], "rolls": 1.0 } ] }
๐Ÿ’ก looting_enchant adds extra drops for each level of the Looting enchantment. So a Looting III sword will drop up to 6 Crystal Gems from one frog! That rewards players for crafting the Crystal Sword from Episode 4.
โ„น๏ธ The folder is loot_table/entities/ not loot_table/blocks/ make sure you're in the right place!
Created the entities folder inside loot_table
Created crystal_frog.json loot table
10
๐ŸŒ
Natural Spawning
Make frogs spawn automatically in swamp biomes
Locked
๐ŸŒ
Goal Crystal Frogs appear naturally in the world

Use Fabric's BiomeModifications API to add the Crystal Frog as a natural spawn in swamp biomes. This means players can find them exploring the world not just with a spawn egg!

Add natural spawning in CrystalMod.java

CrystalMod.java โ€” add these imports
import net.fabricmc.fabric.api.biome.v1.BiomeModifications; import net.fabricmc.fabric.api.biome.v1.BiomeSelectors; import net.minecraft.entity.SpawnGroup; import net.minecraft.world.biome.BiomeKeys;
CrystalMod.java โ€” add inside onInitialize() at the bottom
// Make Crystal Frogs spawn naturally in swamp biomes BiomeModifications.addSpawn( BiomeSelectors.includeByKey(BiomeKeys.SWAMP, BiomeKeys.MANGROVE_SWAMP), SpawnGroup.CREATURE, ModEntities.CRYSTAL_FROG, 8, // weight (higher = more common) 2, // minGroupSize 4 // maxGroupSize );
โ„น๏ธ Weight 8 is similar to vanilla frogs (weight 10). Pigs spawn at weight 10, chickens at 8. Higher weight = appears more frequently in the natural spawn pool.
๐Ÿ’ก Want Crystal Frogs to spawn somewhere else? You can try BiomeKeys.JUNGLE, BiomeKeys.LUSH_CAVES, or even BiomeSelectors.all() to spawn them everywhere!
Added the four new imports to CrystalMod.java
Added BiomeModifications.addSpawn() call in onInitialize()
11
โ–ถ๏ธ
Run & Test!
Launch Minecraft and find your Crystal Frog
Locked
๐Ÿธ
Goal โ€” see your Crystal Frog hopping around!

Build the mod and launch Minecraft. Use the spawn egg in creative mode to summon a frog, then hold a Crystal Gem and watch it follow you!

Build and run

  • 1In IntelliJ, open the Gradle panel (right side) โ†’ Tasks โ†’ fabric โ†’ runClient
  • 2Wait for Minecraft to launch (may take 2-3 minutes the first time)
  • 3Create a new Creative world
  • 4Open inventory โ†’ search for "Crystal Frog Spawn Egg" โ†’ place it in your hotbar
  • 5Right-click on the ground to spawn a Crystal Frog
  • 6Hold a Crystal Gem โ€” the frog should walk towards you!
โš ๏ธ Build error? Check: (1) Did you add FabricDefaultAttributeRegistry.register() in CrystalMod.java? (2) Did you add the client entry point to fabric.mod.json? (3) Are there any red squiggles in IntelliJ? Fix those first.
๐Ÿ’ก Frog is invisible? That usually means the renderer isn't registered. Check: (1) CrystalModClient.java exists and implements ClientModInitializer, (2) "client" entry point is in fabric.mod.json, (3) the texture file path is exactly textures/entity/crystal_frog.png.
๐ŸŒŸ Challenge! Can you make the Crystal Frog drop a Magic Cookie (from Episode 3) instead of a Crystal Gem? Hint: change the item name in the loot table JSON to "crystalmod:magic_cookie".
Mod builds without errors in IntelliJ
Crystal Frog Spawn Egg appears in creative inventory
Spawned a frog and it's visible (has texture)
Frog follows me when I hold a Crystal Gem
Frog drops Crystal Gems when defeated
๐Ÿธ ๐Ÿ’Ž ๐ŸŽ‰ ๐Ÿ’š ๐Ÿธ
Crystal Frog Lives!

You created a brand new mob a living entity with AI, a 3D model, a texture, a spawn egg, loot, and natural spawning. That's serious modding! One episode left the Lucky Crystal Block!

Episode 6: Lucky Crystal Block ๐Ÿ€ โ†’ ⭐ View My Progress & Certificates

This workshop was free and took many hours to build. If it helped you learn something new, consider supporting the project.

☕ Support on Ko-fi