Screens & Rendering

Drawing a Sprite

Sprites are the building blocks of 2D MonoGame rendering. This tutorial shows how to load a Texture2D, draw it with SpriteBatch, and use tinting, scaling, rotation, flipping, sorting, and generated textures for examples that run even when you have no image files yet.

Download the runnable sample project

Use the verified ZIP bundle for this article to review the extracted example files, run the sample host, and repackage the sources without bin or obj folders.

The SpriteBatch cycle

SpriteBatch batches many 2D draw requests and sends them to the GPU efficiently. The pattern is always Begin, one or more Draw calls, then End. Create the batch once in LoadContent; do not recreate it every frame.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

public class Game1 : Game
{
    private GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch;
    private Texture2D _playerTexture;
    private Vector2 _playerPosition = new Vector2(100, 100);

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);
        _playerTexture = Content.Load<Texture2D>("player");
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        _spriteBatch.Begin();
        _spriteBatch.Draw(_playerTexture, _playerPosition, Color.White);
        _spriteBatch.End();
        base.Draw(gameTime);
    }
}

💡 Tip: Color.White keeps the texture's original colours. Any other colour tints the sprite by multiplying the texture colour by that tint.

Loading textures with the content pipeline

For production art, add a PNG to your MonoGame content project and load it by asset name. If the file is Content/player.png and its asset name is player, load it with Content.Load<Texture2D>("player"). Do not include the file extension in the load name.

⚠️ Caution: The PNG must be added to Content.mgcb with the MGCB Editor and built by the content pipeline. Copying an image beside Game1.cs is not enough. Open the MGCB Editor, choose Add Existing Item, select the PNG, keep the Texture Importer and Texture Processor, save, then build.

private Texture2D _playerTexture;
private Vector2 _playerPosition = new Vector2(240, 160);

protected override void LoadContent()
{
    _spriteBatch = new SpriteBatch(GraphicsDevice);

    // Loads Content/player.png after it has been processed by Content.mgcb.
    _playerTexture = Content.Load<Texture2D>("player");
}

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    _spriteBatch.Begin(samplerState: SamplerState.PointClamp);
    _spriteBatch.Draw(_playerTexture, _playerPosition, Color.White);
    _spriteBatch.Draw(_playerTexture, _playerPosition + new Vector2(96, 0), Color.Orange);
    _spriteBatch.End();

    base.Draw(gameTime);
}

The full Draw overload

The most useful Draw overload lets you choose the position, optional source rectangle, tint colour, rotation, origin, scale, flip effect, and layer depth. To rotate around the sprite centre, set origin to half the source rectangle size.

Rectangle? sourceRectangle = null;
Vector2 position = new Vector2(320, 180);
Vector2 origin = new Vector2(_playerTexture.Width / 2f, _playerTexture.Height / 2f);
SpriteEffects effects = SpriteEffects.FlipHorizontally;

_spriteBatch.Draw(
    texture: _playerTexture,
    position: position,
    sourceRectangle: sourceRectangle,
    color: Color.DeepSkyBlue,
    rotation: MathHelper.ToRadians(25f),
    origin: origin,
    scale: 2f,
    effects: effects,
    layerDepth: 0.2f);

⚠️ Warning: Rotation happens around origin, not automatically around the centre. If origin is Vector2.Zero, the sprite spins around its top-left corner.

Generating textures in code

Generated textures are perfect for placeholders, debug shapes, particles, and tutorials. A 1×1 white pixel can be stretched into rectangles, while a checkerboard texture shows how to fill a whole texture with SetData(Color[]).

private Texture2D CreateWhitePixel(GraphicsDevice graphicsDevice)
{
    var pixel = new Texture2D(graphicsDevice, 1, 1);
    pixel.SetData(new[] { Color.White });
    return pixel;
}

private Texture2D CreateCheckerboard(
    GraphicsDevice graphicsDevice,
    int size,
    Color first,
    Color second)
{
    var texture = new Texture2D(graphicsDevice, size, size);
    var pixels = new Color[size * size];

    for (int y = 0; y < size; y++)
    {
        for (int x = 0; x < size; x++)
        {
            bool useFirst = ((x / 4) + (y / 4)) % 2 == 0;
            pixels[y * size + x] = useFirst ? first : second;
        }
    }

    texture.SetData(pixels);
    return texture;
}

💡 Tip: When drawing generated pixel art or tile textures, use SamplerState.PointClamp in Begin so scaled pixels stay crisp instead of blurry.

Sort modes and blend states

SpriteBatch.Begin accepts render settings for the whole batch. SpriteSortMode.Deferred is the usual default and is fastest for many simple sprites. Use SpriteSortMode.BackToFront when layerDepth should decide draw order: sprites nearer 1f draw first, sprites nearer 0f draw later and appear in front.

BlendState.AlphaBlend is the normal choice for PNG sprites with transparency. BlendState.Additive is useful for glows, fire, magic, and particles; BlendState.Opaque skips transparency when you know everything is solid.

_spriteBatch.Begin(
    sortMode: SpriteSortMode.BackToFront,
    blendState: BlendState.AlphaBlend,
    samplerState: SamplerState.PointClamp);

// Back layer: drawn first.
_spriteBatch.Draw(_backgroundTile, new Rectangle(0, 0, 640, 360), null, Color.White, 0f, Vector2.Zero, SpriteEffects.None, 1f);

// Front layer: drawn later because 0 is closer to the camera.
_spriteBatch.Draw(_playerTexture, _playerPosition, null, Color.White, 0f, _playerOrigin, 2f, SpriteEffects.None, 0f);

_spriteBatch.End();

Complete example: generated tiles and rotating sprite

This complete Game1.cs creates all textures in code. It draws a procedural checkerboard tile grid as the background, then draws a rotating, scaled sprite with a centre origin, tint colour, optional horizontal flip, and explicit layerDepth.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

public class Game1 : Game
{
    private readonly GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch;
    private Texture2D _checkerTile;
    private Texture2D _playerTexture;
    private Vector2 _playerPosition;
    private Vector2 _playerOrigin;
    private float _rotation;

    public Game1()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        IsMouseVisible = true;
    }

    protected override void Initialize()
    {
        _graphics.PreferredBackBufferWidth = 960;
        _graphics.PreferredBackBufferHeight = 540;
        _graphics.ApplyChanges();

        _playerPosition = new Vector2(480, 270);
        base.Initialize();
    }

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);
        _checkerTile = CreateCheckerboard(GraphicsDevice, 16, Color.DarkSlateBlue, Color.MidnightBlue);
        _playerTexture = CreateDiamondSprite(GraphicsDevice, 64);
        _playerOrigin = new Vector2(_playerTexture.Width / 2f, _playerTexture.Height / 2f);
    }

    protected override void Update(GameTime gameTime)
    {
        if (Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();

        float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
        _rotation += MathHelper.ToRadians(90f) * dt;
        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.Black);

        _spriteBatch.Begin(
            sortMode: SpriteSortMode.BackToFront,
            blendState: BlendState.AlphaBlend,
            samplerState: SamplerState.PointClamp);

        DrawBackgroundTiles();

        SpriteEffects flip = (System.MathF.Sin(_rotation) > 0f)
            ? SpriteEffects.FlipHorizontally
            : SpriteEffects.None;

        _spriteBatch.Draw(
            texture: _playerTexture,
            position: _playerPosition,
            sourceRectangle: null,
            color: Color.LightSkyBlue,
            rotation: _rotation,
            origin: _playerOrigin,
            scale: 2f,
            effects: flip,
            layerDepth: 0.1f);

        _spriteBatch.End();
        base.Draw(gameTime);
    }

    private void DrawBackgroundTiles()
    {
        const int tileSize = 64;
        int columns = GraphicsDevice.Viewport.Width / tileSize + 1;
        int rows = GraphicsDevice.Viewport.Height / tileSize + 1;

        for (int y = 0; y < rows; y++)
        {
            for (int x = 0; x < columns; x++)
            {
                var destination = new Rectangle(x * tileSize, y * tileSize, tileSize, tileSize);
                _spriteBatch.Draw(_checkerTile, destination, null, Color.White, 0f, Vector2.Zero, SpriteEffects.None, 1f);
            }
        }
    }

    private static Texture2D CreateCheckerboard(GraphicsDevice graphicsDevice, int size, Color first, Color second)
    {
        var texture = new Texture2D(graphicsDevice, size, size);
        var pixels = new Color[size * size];

        for (int y = 0; y < size; y++)
        {
            for (int x = 0; x < size; x++)
            {
                bool useFirst = ((x / 4) + (y / 4)) % 2 == 0;
                pixels[y * size + x] = useFirst ? first : second;
            }
        }

        texture.SetData(pixels);
        return texture;
    }

    private static Texture2D CreateDiamondSprite(GraphicsDevice graphicsDevice, int size)
    {
        var texture = new Texture2D(graphicsDevice, size, size);
        var pixels = new Color[size * size];
        Vector2 centre = new Vector2((size - 1) / 2f, (size - 1) / 2f);
        float radius = size * 0.42f;

        for (int y = 0; y < size; y++)
        {
            for (int x = 0; x < size; x++)
            {
                float distance = System.MathF.Abs(x - centre.X) + System.MathF.Abs(y - centre.Y);
                pixels[y * size + x] = distance < radius ? Color.White : Color.Transparent;
            }
        }

        texture.SetData(pixels);
        return texture;
    }
}
dotnet build
dotnet run

Common pitfalls

⚠️ Caution: Keep one Begin/End pair for sprites that share the same blend, sampler, and sort settings. If you need additive particles, end the alpha batch first, start a new additive batch, then end it before drawing normal sprites again.

Next steps

Need larger tap targets or higher contrast? Toggle enhanced spacing and high contrast in the header. Your preference is saved in session storage.

🎓 Test Your Knowledge

Check your understanding of the key concepts from this tutorial.

What MonoGame class is used to draw 2D sprites?

In what order must you call SpriteBatch methods?

What does the color parameter in SpriteBatch.Draw do when set to Color.White?

What is a sourceRectangle in SpriteBatch.Draw?