diff --git a/src/java/mmm/materials/Materials.java b/src/java/mmm/materials/Materials.java
index 0922e1e..2b61609 100644
--- a/src/java/mmm/materials/Materials.java
+++ b/src/java/mmm/materials/Materials.java
@@ -46,6 +46,9 @@ public class Materials
 		URegistry.addBlock( ORE_COPPER = new MOCopper( ) );
 		URegistry.addBlock( ORE_MALACHITE = new MOMalachite( ) );
 		URegistry.addBlock( ORE_CUPRITE = new MOCuprite( ) );
+
+		// Alloy recipes
+		MAlloyRecipesRegistry.INSTANCE.addRecipe( 1600 , 0.1f , ITEM_COKE , Items.COAL );
 	}
 
 
diff --git a/src/java/mmm/tech/base/TBAlloyFurnaceBlock.java b/src/java/mmm/tech/base/TBAlloyFurnaceBlock.java
index ff1745f..82505ff 100644
--- a/src/java/mmm/tech/base/TBAlloyFurnaceBlock.java
+++ b/src/java/mmm/tech/base/TBAlloyFurnaceBlock.java
@@ -6,6 +6,7 @@ import java.util.Random;
 
 import javax.annotation.Nullable;
 
+import mmm.Mmm;
 import mmm.utils.UMaths;
 import mmm.utils.URegistry;
 import net.minecraft.block.Block;
@@ -19,12 +20,15 @@ import net.minecraft.block.state.IBlockState;
 import net.minecraft.creativetab.CreativeTabs;
 import net.minecraft.entity.Entity;
 import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.init.SoundEvents;
 import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
 import net.minecraft.tileentity.TileEntity;
 import net.minecraft.util.BlockRenderLayer;
 import net.minecraft.util.EnumBlockRenderType;
 import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
 import net.minecraft.util.EnumParticleTypes;
 import net.minecraft.util.Mirror;
 import net.minecraft.util.Rotation;
@@ -255,4 +259,21 @@ public class TBAlloyFurnaceBlock
 		return side == EnumFacing.DOWN;
 	}
 
+
+	// *************************************************************************************************
+	// INTERACTION
+	// *************************************************************************************************
+
+	@Override
+	public boolean onBlockActivated( World worldIn , BlockPos pos , IBlockState state , EntityPlayer playerIn ,
+			EnumHand hand , ItemStack heldItem , EnumFacing side , float hitX , float hitY , float hitZ )
+	{
+		TileEntity te = worldIn.getTileEntity( pos );
+		if ( !(te instanceof TBAlloyFurnaceTileEntity ) || playerIn.isSneaking( ) ) {
+			return false;
+		}
+		playerIn.openGui( Mmm.get( ) , 0 , worldIn , pos.getX( ) , pos.getY( ) , pos.getZ( ) );
+		return true;
+	}
+
 }
diff --git a/src/java/mmm/tech/base/TBAlloyFurnaceContainer.java b/src/java/mmm/tech/base/TBAlloyFurnaceContainer.java
new file mode 100644
index 0000000..33a6f2b
--- /dev/null
+++ b/src/java/mmm/tech/base/TBAlloyFurnaceContainer.java
@@ -0,0 +1,64 @@
+package mmm.tech.base;
+
+
+import mmm.utils.UContainers;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.Slot;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+
+
+public class TBAlloyFurnaceContainer
+		extends Container
+{
+	private final World world;
+	private final BlockPos position;
+
+	// public final IInventory input;
+	// public final IInventory fuel;
+	// public final IInventory output;
+
+
+	@SideOnly( Side.CLIENT )
+	public TBAlloyFurnaceContainer( final InventoryPlayer playerInv , final World world )
+	{
+		this( playerInv , world , BlockPos.ORIGIN );
+	}
+
+
+	public TBAlloyFurnaceContainer( final InventoryPlayer playerInv , final World world , final BlockPos blockPos )
+	{
+		this.world = world;
+		this.position = blockPos;
+
+		// this.input = input;
+		// this.fuel = fuel;
+
+		// UContainers.addGrid( //
+		// ( i , x , y ) -> this.addSlotToContainer( new Slot( input , i , x , y ) ) , //
+		// 3 , 5 , 0 , 11 , 7 );
+
+		// UContainers.addGrid( //
+		// ( i , x , y ) -> this.addSlotToContainer( new SlotFurnaceFuel( fuel , i , x , y ) ) , //
+		// 2 , 2 , 0 , 79 , 61 );
+
+		UContainers.addPlayerInventory( //
+				( i , x , y ) -> this.addSlotToContainer( new Slot( playerInv , i , x , y ) ) , //
+				7 , 111 );
+	}
+
+
+	@Override
+	public boolean canInteractWith( final EntityPlayer player )
+	{
+		return this.world.getBlockState( this.position ).getBlock( ) instanceof TBAlloyFurnaceBlock
+				&& player.getDistanceSq( this.position.getX( ) + .5 , this.position.getY( ) + .5 ,
+						this.position.getZ( ) + .5 ) <= 64.;
+	}
+
+}
diff --git a/src/java/mmm/tech/base/TBAlloyFurnaceGui.java b/src/java/mmm/tech/base/TBAlloyFurnaceGui.java
new file mode 100644
index 0000000..f2c27ea
--- /dev/null
+++ b/src/java/mmm/tech/base/TBAlloyFurnaceGui.java
@@ -0,0 +1,108 @@
+package mmm.tech.base;
+
+
+import mmm.Mmm;
+import net.minecraft.client.gui.inventory.GuiContainer;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.world.World;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+
+
+@SideOnly( Side.CLIENT )
+public class TBAlloyFurnaceGui
+		extends GuiContainer
+{
+	private static enum Tab {
+		MAIN( 207 , 0 ) ,
+		CONFIG( 191 , 0 );
+
+		public final int iconX;
+		public final int iconY;
+
+
+		private Tab( final int iconX , final int iconY )
+		{
+			this.iconX = iconX;
+			this.iconY = iconY;
+		}
+	}
+
+	private static final ResourceLocation TEXTURES[] = {
+			new ResourceLocation( Mmm.ID , "textures/gui/alloy_furnace_1.png" ) ,
+			new ResourceLocation( Mmm.ID , "textures/gui/alloy_furnace_2.png" )
+	};
+
+	private static final TBAlloyFurnaceGui.Tab TABS[] = TBAlloyFurnaceGui.Tab.values( );
+
+	private static final int TAB_WIDTH = 26;
+	private static final int TAB_HEIGHT = 26;
+	private static final int TAB_BORDER = 4;
+	private static final int TABS_X = 176;
+	private static final int TABS_Y = 45;
+	private static final int TAB_ICON_X = 5;
+	private static final int TAB_ICON_Y = 5;
+	private static final int TAB_ICON_WIDTH = 16;
+	private static final int TAB_ICON_HEIGHT = 16;
+
+	private final TBAlloyFurnaceGui.Tab selectedTab = Tab.MAIN;
+
+
+	public TBAlloyFurnaceGui( final InventoryPlayer inventoryPlayer , final World world )
+	{
+		super( new TBAlloyFurnaceContainer( inventoryPlayer , world ) );
+		this.xSize = 176;
+		this.ySize = 194;
+	}
+
+
+	@Override
+	protected void drawGuiContainerBackgroundLayer( final float partialTicks , final int mouseX , final int mouseY )
+	{
+		GlStateManager.color( 1f , 1f , 1f , 1f );
+		GlStateManager.enableBlend( );
+		GlStateManager.disableLighting( );
+		// RenderHelper.enableGUIStandardItemLighting( );
+
+		// Inactive tab
+		this.mc.getTextureManager( ).bindTexture( TBAlloyFurnaceGui.TEXTURES[ 0 ] );
+		for ( final TBAlloyFurnaceGui.Tab tab : TBAlloyFurnaceGui.TABS ) {
+			if ( tab != this.selectedTab ) {
+				this.drawTab( tab );
+			}
+		}
+
+		// Main panel
+		this.mc.getTextureManager( ).bindTexture( TBAlloyFurnaceGui.TEXTURES[ this.selectedTab.ordinal( ) ] );
+		this.drawTexturedModalRect( this.guiLeft , this.guiTop , 0 , 0 , this.xSize , this.ySize );
+
+		// Active tab
+		this.mc.getTextureManager( ).bindTexture( TBAlloyFurnaceGui.TEXTURES[ 0 ] );
+		this.drawTab( this.selectedTab );
+	}
+
+
+	private void drawTab( final TBAlloyFurnaceGui.Tab tab )
+	{
+		final boolean selected = this.selectedTab == tab;
+		final int tabOffsetX = tab.ordinal( ) * TBAlloyFurnaceGui.TAB_WIDTH;
+		final int tabX = this.guiLeft + tabOffsetX + TBAlloyFurnaceGui.TAB_BORDER;
+		final int tabY = this.guiTop - TBAlloyFurnaceGui.TAB_HEIGHT + TBAlloyFurnaceGui.TAB_BORDER;
+
+		this.drawTexturedModalRect( tabX , tabY , //
+				TBAlloyFurnaceGui.TABS_X + tabOffsetX ,
+				TBAlloyFurnaceGui.TABS_Y + ( selected ? TBAlloyFurnaceGui.TAB_HEIGHT : 0 ) , //
+				TBAlloyFurnaceGui.TAB_WIDTH , TBAlloyFurnaceGui.TAB_HEIGHT );
+
+		this.zLevel = 100f;
+		this.drawTexturedModalRect( tabX + TBAlloyFurnaceGui.TAB_ICON_X , tabY + TBAlloyFurnaceGui.TAB_ICON_Y , //
+				tab.iconX , tab.iconY , //
+				TBAlloyFurnaceGui.TAB_ICON_WIDTH , TBAlloyFurnaceGui.TAB_ICON_HEIGHT );
+
+		this.zLevel = 0;
+	}
+
+}
diff --git a/src/java/mmm/tech/base/TBAlloyFurnaceGuiHandler.java b/src/java/mmm/tech/base/TBAlloyFurnaceGuiHandler.java
new file mode 100644
index 0000000..06c3c95
--- /dev/null
+++ b/src/java/mmm/tech/base/TBAlloyFurnaceGuiHandler.java
@@ -0,0 +1,41 @@
+package mmm.tech.base;
+
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+import net.minecraftforge.fml.common.network.IGuiHandler;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+
+
+public class TBAlloyFurnaceGuiHandler
+		implements IGuiHandler
+{
+
+	@Override
+	public Object getServerGuiElement( int ID , EntityPlayer player , World world , int x , int y , int z )
+	{
+		BlockPos pos = new BlockPos( x , y , z );
+		TileEntity tileEntity = world.getTileEntity( pos );
+		if ( tileEntity instanceof TBAlloyFurnaceTileEntity ) {
+			return new TBAlloyFurnaceContainer( player.inventory , world , pos );
+		}
+		return null;
+	}
+
+
+	@Override
+	@SideOnly( Side.CLIENT )
+	public Object getClientGuiElement( int ID , EntityPlayer player , World world , int x , int y , int z )
+	{
+		TileEntity tileEntity = world.getTileEntity( new BlockPos( x , y , z ) );
+		if ( tileEntity instanceof TBAlloyFurnaceTileEntity ) {
+			return new TBAlloyFurnaceGui( player.inventory , world );
+		}
+		return null;
+	}
+
+}
diff --git a/src/java/mmm/tech/base/TechBase.java b/src/java/mmm/tech/base/TechBase.java
index aa023a2..1a76e55 100644
--- a/src/java/mmm/tech/base/TechBase.java
+++ b/src/java/mmm/tech/base/TechBase.java
@@ -1,9 +1,11 @@
 package mmm.tech.base;
 
 
+import mmm.Mmm;
 import mmm.utils.URegistry;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemBlockSpecial;
+import net.minecraftforge.fml.common.network.NetworkRegistry;
 import net.minecraftforge.fml.common.registry.GameRegistry;
 
 
@@ -24,6 +26,7 @@ public class TechBase
 		URegistry.addBlock( TechBase.ALLOY_FURNACE_BLOCK_INACTIVE , TechBase.ALLOY_FURNACE_ITEM );
 		URegistry.addBlock( TechBase.ALLOY_FURNACE_BLOCK_ACTIVE , null );
 		GameRegistry.registerTileEntity( TBAlloyFurnaceTileEntity.class , "mmm:tech/base/alloy_furnace" );
+		NetworkRegistry.INSTANCE.registerGuiHandler( Mmm.get( ) , new TBAlloyFurnaceGuiHandler( ) );
 	}
 
 
diff --git a/src/java/mmm/utils/A_UInventoryGrid.java b/src/java/mmm/utils/A_UInventoryGrid.java
new file mode 100644
index 0000000..68f4db0
--- /dev/null
+++ b/src/java/mmm/utils/A_UInventoryGrid.java
@@ -0,0 +1,178 @@
+package mmm.utils;
+
+
+import java.util.Arrays;
+
+import javax.annotation.Nullable;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.ItemStackHelper;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.text.ITextComponent;
+import net.minecraft.util.text.TextComponentTranslation;
+
+
+
+public abstract class A_UInventoryGrid
+		implements IInventory
+{
+	public final String name;
+	public final int width;
+	public final int height;
+	public final int slotsCount;
+	private final ItemStack[] inventoryContents;
+
+
+	public A_UInventoryGrid( final String name , final int width , final int height )
+	{
+		this.name = name;
+		this.width = width;
+		this.height = height;
+		this.slotsCount = width * height;
+		this.inventoryContents = new ItemStack[ width * height ];
+	}
+
+
+	@Override
+	public String getName( )
+	{
+		return this.name;
+	}
+
+
+	@Override
+	public boolean hasCustomName( )
+	{
+		return false;
+	}
+
+
+	@Override
+	public ITextComponent getDisplayName( )
+	{
+		return new TextComponentTranslation( this.name );
+	}
+
+
+	@Override
+	public int getSizeInventory( )
+	{
+		return this.slotsCount;
+	}
+
+
+	@Override
+	@Nullable
+	public ItemStack getStackInSlot( final int index )
+	{
+		return index >= 0 && index < this.inventoryContents.length ? this.inventoryContents[ index ] : null;
+	}
+
+
+	@Nullable
+	public ItemStack getStackInCell( final int x , final int y )
+	{
+		return this.getStackInSlot( x + y * this.width );
+	}
+
+
+	@Override
+	@Nullable
+	public ItemStack decrStackSize( final int index , final int count )
+	{
+		final ItemStack itemstack = ItemStackHelper.getAndSplit( this.inventoryContents , index , count );
+		if ( itemstack != null ) {
+			this.markDirty( );
+		}
+		return itemstack;
+	}
+
+
+	@Override
+	@Nullable
+	public ItemStack removeStackFromSlot( final int index )
+	{
+		if ( this.inventoryContents[ index ] != null ) {
+			final ItemStack itemstack = this.inventoryContents[ index ];
+			this.inventoryContents[ index ] = null;
+			return itemstack;
+		}
+		return null;
+	}
+
+
+	@Override
+	public void setInventorySlotContents( final int index , final ItemStack stack )
+	{
+		this.inventoryContents[ index ] = stack;
+		if ( stack != null && stack.stackSize > this.getInventoryStackLimit( ) ) {
+			stack.stackSize = this.getInventoryStackLimit( );
+		}
+		this.markDirty( );
+	}
+
+
+	@Override
+	public int getInventoryStackLimit( )
+	{
+		return 64;
+	}
+
+
+	@Override
+	public boolean isUseableByPlayer( final EntityPlayer player )
+	{
+		return true;
+	}
+
+
+	@Override
+	public void openInventory( final EntityPlayer player )
+	{
+		// EMPTY
+	}
+
+
+	@Override
+	public void closeInventory( final EntityPlayer player )
+	{
+		// EMPTY
+	}
+
+
+	@Override
+	public boolean isItemValidForSlot( final int index , final ItemStack stack )
+	{
+		return true;
+	}
+
+
+	@Override
+	public int getField( final int id )
+	{
+		return 0;
+	}
+
+
+	@Override
+	public void setField( final int id , final int value )
+	{
+		// EMPTY
+	}
+
+
+	@Override
+	public int getFieldCount( )
+	{
+		return 0;
+	}
+
+
+	@Override
+	public void clear( )
+	{
+		Arrays.fill( this.inventoryContents , null );
+	}
+
+}
diff --git a/src/java/mmm/utils/UContainers.java b/src/java/mmm/utils/UContainers.java
new file mode 100644
index 0000000..1641574
--- /dev/null
+++ b/src/java/mmm/utils/UContainers.java
@@ -0,0 +1,36 @@
+package mmm.utils;
+
+public class UContainers
+{
+
+	public static interface SlotAdder
+	{
+		public void addSlot( int index , int x , int y );
+	}
+
+
+	public static void addPlayerInventory( final UContainers.SlotAdder slotAdder , final int x , final int y )
+	{
+		UContainers.addPlayerInventory( slotAdder , x , y , 4 );
+	}
+
+
+	public static void addPlayerInventory( final UContainers.SlotAdder slotAdder , final int x , final int y ,
+			final int spacing )
+	{
+		UContainers.addGrid( slotAdder , 9 , 3 , 9 , x , y ); // Main inventory
+		UContainers.addGrid( slotAdder , 9 , 1 , 0 , x , y + spacing + 54 ); // Quick bar
+	}
+
+
+	public static void addGrid( final UContainers.SlotAdder slotAdder , final int columns , final int rows ,
+			final int index , final int x , final int y )
+	{
+		for ( int row = 0 ; row < rows ; ++row ) {
+			for ( int column = 0 ; column < columns ; ++column ) {
+				slotAdder.addSlot( index + column + row * 9 , x + column * 18 , y + row * 18 );
+			}
+		}
+	}
+
+}