LCD Custom Character: Ultimate Guide to Create Custom Icons

Isabella Sep 08, 2026

An LCD custom character is a user-defined glyph that you can draw, store, and display on a character LCD beyond the standard ASCII font. Instead of accepting only letters, numbers, and a handful of symbols, many HD44780-compatible displays let you define your own icons, arrows, battery bars, smileys, logos, or non-English letters. That flexibility turns a simple 16x2 or 20x4 display into a more expressive interface for embedded projects.

How to Create Custom Character for LCD
How to Create Custom Character for LCD

For makers and firmware developers, learning how LCD custom characters work is a practical skill. It helps you save screen space, communicate status quickly, and add polish to menus, sensors, and control panels without upgrading to a graphical display. This guide walks through the memory model, the 5x8 pixel grid, Arduino-style code, and the real limits you should know before designing a full icon set.

How to control a character I2C LCD with Arduino
How to control a character I2C LCD with Arduino

How LCD Custom Characters Work

Most character LCDs use an HD44780-compatible controller or a clone. That controller contains a character generator ROM, usually called CGROM, which holds the built-in font. It also contains a small character generator RAM, or CGRAM, where user-defined patterns live. The CGROM gives you the standard characters; the CGRAM gives you the custom ones.

OS tan xbox
OS tan xbox

Because CGRAM is RAM, the custom patterns can be changed while the program is running. You are not permanently burning new letters into the display. You can upload one set of icons at startup, replace a slot later, or animate a small symbol by rewriting its bytes in a loop. This makes LCD custom characters useful for both static icons and simple motion effects.

The 5x8 Pixel Matrix and CGRAM

Legend of the Inbox - dazOS_
Legend of the Inbox - dazOS_

Each custom character is built from a small pixel grid, typically 5 columns wide and 8 rows tall. In CGRAM, one custom character occupies eight bytes. Each byte represents one row, and the lower five bits of that byte decide which pixels are on. A bit value of 1 turns a pixel on, while 0 leaves it off. The display reads the rows from top to bottom, so the first byte is the top row and the eighth byte is the bottom row.

Consider a simple heart pattern. The rows might be 00000, 01010, 11111, 11111, 01110, 00100, 00000, and 00000. Converted to hexadecimal, those rows become 0x00, 0x0A, 0x1F, 0x1F, 0x0E, 0x04, 0x00, and 0x00. That compact byte array is all the controller needs to draw a recognizable heart on the LCD.

Addressing and Displaying Custom Characters

LILI - virtual DJ
LILI - virtual DJ

Custom character slots are normally numbered 0 through 7, giving you eight user-defined patterns at a time. After you store a pattern in slot 0, you display it by writing character code 0 to the LCD. In the Arduino LiquidCrystal library, that usually means calling lcd.createChar(0, heart); during setup and then lcd.write(byte(0)); where you want the icon to appear.

On I2C LCDs, the same underlying HD44780 controller is still doing the work. The I2C backpack only reduces the number of pins required, so libraries such as LiquidCrystal_I2C still support createChar. If you are driving the display directly, you send commands to set the CGRAM address and then write the eight data bytes. The result is the same: a custom character becomes available just like a normal font character.

Creating and Using LCD Custom Characters in Practice

the back side of an old computer screen with various stickers and logos on it
the back side of an old computer screen with various stickers and logos on it

To design a custom character, start with a 5x8 pixel grid and sketch the shape you want. Online LCD custom character generators are popular because they let you click pixels and automatically convert the drawing into a byte array. You can also draw the pattern by hand on graph paper, but keep the design simple. At only 5 pixels wide, fine details disappear quickly, so bold outlines and clear silhouettes work best.

Once the pattern is converted into eight bytes, your code uploads it to the display controller. The LCD must be initialized before you create the custom characters, and each pattern should be assigned to a unique slot. If your project needs more than eight icons, you can reuse slots dynamically. For example, you might show a battery icon in slot 0, then redefine slot 0 later as a temperature icon when the screen changes.

cute strawberry hair clips ♡
cute strawberry hair clips ♡
an old - school computer screen with many different emulators
an old - school computer screen with many different emulators
Custom Character Generation on 16x2 char lcd
Custom Character Generation on 16x2 char lcd
crt lcd difference
crt lcd difference
Instagram Y2k Avatar, Y2k Game Characters, Low Poly Art Style, Low Poly Character Reference, 3d Y2k Art, Y2k Game, Y2k Avatar Creator, Y2k 3d Model, Y2k 3d Art
Instagram Y2k Avatar, Y2k Game Characters, Low Poly Art Style, Low Poly Character Reference, 3d Y2k Art, Y2k Game, Y2k Avatar Creator, Y2k 3d Model, Y2k 3d Art
my neocities
my neocities
8 bit
8 bit
Pixelated Character Design, Pixel Outfit Design, 2d Pixel Character Design, Green Pixel Character Design, Pixel Character Hair, 16 Bit Pixel Art Character Design, Pixel Oc, Pixel Art Outfit, Anime Character Pixel Design
Pixelated Character Design, Pixel Outfit Design, 2d Pixel Character Design, Green Pixel Character Design, Pixel Character Hair, 16 Bit Pixel Art Character Design, Pixel Oc, Pixel Art Outfit, Anime Character Pixel Design
an image of a video game character in the style of roby mantoba
an image of a video game character in the style of roby mantoba
an image of a desktop computer screen with green and black graphics on the wallpaper
an image of a desktop computer screen with green and black graphics on the wallpaper

Step-by-Step Arduino LCD Custom Character Example

Start by defining byte arrays for each icon. A heart might look like byte heart[8] = {0b00000, 0b01010, 0b11111, 0b11111, 0b01110, 0b00100, 0b00000, 0b00000};. A smiley can follow the same structure with different rows. Clear names make the code easier to maintain, especially when you have several custom characters in one project.

In the setup function, initialize the LCD, then upload the patterns. For a standard parallel LCD, the sequence might be lcd.begin(16, 2); lcd.createChar(0, heart); lcd.createChar(1, smiley);. To display them, set the cursor and write the slot number: lcd.setCursor(0, 0); lcd.write(byte(0)); lcd.setCursor(1, 0); lcd.write(byte(1));. In the loop, you can update sensor readings or menu states while leaving the custom icons in place.

Practical Applications and Limitations

LCD custom characters are ideal for battery indicators, signal strength bars, progress meters, arrows, locks, clocks, thermometers, and simple logos. They also help with languages that need accented letters or symbols not present in the display's built-in font. In a weather station, for instance, you might combine a custom raindrop, sun, and cloud icon with numeric readings to create a clearer dashboard.

The main limitations are memory and resolution. Most HD44780-compatible LCDs allow only eight custom characters at once, and each one is limited to a 5x8 monochrome grid. There is no grayscale or anti-aliasing, so curves can look blocky. Also, redefining a slot changes every instance of that character code currently on the screen. You can animate by rewriting CGRAM quickly, but excessive updates can cause flicker or timing issues on slower interfaces.

With a few byte patterns and a bit of experimentation, you can turn a basic character LCD into a more expressive interface. Try sketching a custom icon for your next project, upload it to CGRAM, and see how much personality a small 5x8 grid can add. The best custom character set is usually the one shaped around your specific display layout and the information your users need at a glance.