Minecraft Alpha Animated Logo

In Minecraft Alpha v1.0.4, the previously static logo on the title screen was replaced with a new animated logo, consisting of a bunch of falling blocks that form the letters of the game’s title. This logo was part of the title screen until Beta 1.4, less than a year later, where it was replaced with the newer one we are all familiar with. Although the later logo looks a lot better than the comparatively basic one used by the game during its very early years, I still think the falling blocks effect is fun and charming, and I want to investigate how it was accomplished.

Interactive Demo

Here is a recreation of the logo, ported to WebGL. There are some additional options you can tweak to see how the effect works. The three checkboxes enable/disable the three separate passes of drawing blocks, and the rotation slider adds a rotation effect which may have been part of the logo during its development.

Code Analysis

Luckily, a recently discovered developer version of Beta 1.2_02 uses this animated logo, and also includes unobfuscated code so we can easily look into and read the code. Interestingly, this unreleased version replaces the letter E with an F so the title reads “Minfcraft”, presumably so it wasn’t mistaken for an actual version of the game. This isn’t seen without editing the code though, since the title screen was changed to immediately load into a world upon startup.

The following code is decompiled from this build of the game. I’ve added some comments and renamed some local variables to make it more readable, but this is more or less what the original source code looked like.

All of the logo related code is contained within the TitleScreen class. TitleScreen contains a private inner LetterBlock class to represent each falling block. Instances of LetterBlock track the downwards motion of each block, updated with each game tick.

private class LetterBlock {
    public double y;  // Height of the block
    public double yO; // Previous y position
    public double ya; // Downward velocity
    
    public LetterBlock(int x, int z) {
        // Choose a random height based on the block's position in the logo
        y = yO = (10 + z) + TitleScreen.random.nextDouble() * 32.0D + x;
    }
    
    public void tick() {
        // Update the value of the old position
        yO = y;
    
        // Accelerate downwards if not touching ground
        if (y > 0.0D)
            ya -= 0.6D; 
    
        y += ya; // Add downwards velocity to the y position
        ya *= 0.9D; // Add some 'air resistance' to the downward motion
  
        // If the ground was hit, stop moving
        if (y < 0.0D) {
            y = 0.0D;
            ya = 0.0D;
        } 
    }
}

The height is initialized with a random value, which takes the block’s coordinate in the block grid as input. This causes blocks that are lower down to fall in last, and also causes the logo to generally form from left to right.

Note the yO variable, which keeps track of the previous y position of the block. Minecraft’s game logic is run in ticks, which only occur 20 times per second. If the logo was rendered using only the y variable, the motion would appear choppy since it is only updated at 20fps. To provide smooth motion without needing to update the game logic any faster, the position is linearly interpolated on each frame between the previous and current tick. Variables with an O suffix like this are used in many other places in Minecraft’s codebase for the same reason.

The TitleScreen class itself contains a 2D array of LetterBlocks, and an array of strings that define the shape of the logo.

String[] logo = new String[] {
    " *   * * *   * *** *** *** *** *** ***",
    " ** ** * **  * *   *   * * * * *    * ",
    " * * * * * * * **  *   **  *** **   * ",
    " *   * * *  ** *   *   * * * * *    * ",
    " *   * * *   * *** *** * * * * *    * "
};

private LetterBlock[][] letterBlocks;
private String splash = "missingno";

You can also see here where the “missingno” splash easter egg comes from. If the game is unable to load the contents of splashes.txt, this value won’t get updated and the easter egg is visible.

Rendering

Rendering happens in a method named renderMinecraftLogo, which is called by the TitleScreen’s render method before the menu is drawn. This method takes a single float parameter, which I’ve called tickLerp, that is used to interpolate between the current and last tick.

All logo blocks are drawn three times, which I will call a “pass” or a layer. The first pass draws the shadows underneath the tiles, the second pass draws the falling stone blocks themselves, and the third and final pass draws brightened, translucent blocks that make the stones appear brighter. You can see how each of these passes affect the logo by toggling them on and off in the interactive demo above.

To begin, the rendering loop checks if the letterBlocks array has been initialized, and if not, it initializes each block in the array.

// Set up letter blocks if they haven't been set up yet
if (letterBlocks == null) {
    letterBlocks = new LetterBlock[logo[0].length()][logo.length];
    for (byte row = 0; row < letterBlocks.length; row++) {
        for (byte col = 0; col < (letterBlocks[row]).length; col++) {
            letterBlocks[row][col] = new LetterBlock(row, col); 
        }
    }
} 

Now the actual rendering setup begins. This part sets up the projection matrix, which makes the scene get drawn with a 3D perspective. glViewport is called to constrain rendering to the top of the screen, instead of drawing over the center where the menu buttons are.

// Get ready to set up a new projection matrix
GL11.glMatrixMode(GL_PROJECTION);
GL11.glPushMatrix();
GL11.glLoadIdentity();

// Calculate the GUI upscaling factor
ScreenSizeCalculator screenSizeCalculator = new ScreenSizeCalculator(minecraft.width, minecraft.height);
 // The logo is given 120 pixels of vertical space, multiplied by the GUI scaling factor
int logoHeight = 120 * screenSizeCalculator.scale;

// Project the scene using a 3D perspective
GLU.gluPerspective(70.0F, minecraft.width / logoHeight, 0.05F, 100.0F);

// Constrain rendering to a portion of the top of the screen
GL11.glViewport(0, minecraft.height - logoHeight, minecraft.width, logoHeight);

Then, the modelview matrix, which defines the position, rotation and scale of the camera and the rendered object, is reset.

After setting up, we can start to render each of the passes. The code for drawing the logo is wrapped in a loop, and each iteration has sets some properties for colour blending and positioning.

TileRenderer tileRenderer = new TileRenderer();
for (byte pass = 0; pass < 3; pass++) {
    GL11.glPushMatrix();
    GL11.glTranslatef(0.4F, 0.6F, -13.0F);
    
    // Shadow layer
    if (pass == 0) {
        GL11.glClear(GL_DEPTH_BUFFER_BIT); // Draw everything on top of the menu

        // Shift the camera back a bit, and horizontally squish the scene
        GL11.glTranslatef(0.0F, -0.4F, 0.0F);
        GL11.glScalef(0.98F, 1.0F, 1.0F);

        // Enable alpha blending for translucency effects
        GL11.glEnable(GL_BLEND);
        GL11.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    // Block layer
    if (pass == 1) {
        GL11.glDisable(GL_BLEND); // Disable translucency
        GL11.glClear(GL_DEPTH_BUFFER_BIT); // Draw everything on top of the previous layer
    }
    // Brightness layer
    if (pass == 2) {
        // Enable additive alpha blending
        GL11.glEnable(GL_BLEND);
        GL11.glBlendFunc(GL_SRC_COLOR, GL_ONE);
    }

    ...

Next, a combination of scaling, rotation and translation is used to set up the camera. The first scale operation causes the entire scene to be drawn mirrored along the y axis. The scene is then tilted, and then the whole thing is flattened out and squished horizontally. Lastly, the logo is centered within the viewport.

    GL11.glScalef(1.0F, -1.0F, 1.0F);
    GL11.glRotatef(15.0F, 1.0F, 0.0F, 0.0F);
    GL11.glScalef(0.89F, 1.0F, 0.4F);
    GL11.glTranslatef(-logo[0].length() * 0.5F, -logo.length * 0.5F, 0.0F);

Finally, a texture is chosen for the tiles depending on which pass is currently being rendered. For the two stone layers, this uses the terrain.png texture, which is a texture atlas containing all block textures. The shadows have their own special texture, which is a 16x16 solid black square.

    GL11.glBindTexture(GL_TEXTURE_2D, minecraft.textures.loadTexture("/terrain.png"));

    if (pass == 0)
        GL11.glBindTexture(GL_TEXTURE_2D, minecraft.textures.loadTexture("/title/black.png")); 

Now we can start rendering the blocks themselves. The logo string array from before is iterated over, and the block drawing code is only run if the current character is not a space.

    for (byte y = 0; y < logo.length; y++) {
        for (byte x = 0; x < logo[y].length(); x++) {
            char c = logo[y].charAt(x);
            if (c != ' ') {
                GL11.glPushMatrix();

                LetterBlock letterBlock = letterBlocks[x][y];
                float height = (float)(letterBlock.yO + (letterBlock.y - letterBlock.yO) * tickLerp);
                float scale = 1.0F;
                float alpha = 1.0F;
                float rotation = 0.0F;

                // Special cases for drawing the shadow blocks
                if (pass == 0) {
                    scale = height * 0.04F + 1.0F;
                    alpha = 1.0F / scale;
                    height = 0.0F;
                }

                GL11.glTranslatef(x, y, height);
                GL11.glScalef(scale, scale, scale);
                GL11.glRotatef(rotation, 0.0F, 1.0F, 0.0F);

                tileRenderer.renderCube(Tile.rock, alpha);

                GL11.glPopMatrix();
            }
        }
    }

Four parameters are defined to control how the block is drawn. The first is height, which linearly interpolates between the y and yO values seen before to give smooth motion between game ticks. For shadows, the height is locked at 0.

Next is scale, which is normally 1, but in the case of the shadows, it grows from 1.0 as the height value gets further from 0. There is also an alpha parameter, which controls the transparency of the blocks. This is again only used for the shadows, which become more transparent the higher up the block is.

Finally, there is an unused rotation parameter, which would control the horizontal roll of the blocks. I’ve added a slider to the interactive demo that lets you see what it would look like if this parameter was used. The spinning effect doesn’t work super well with the shadows, so my demo also sets rotation to 0 when drawing the shadows.

And with that, we reach the end of the logo rendering routine!