You can print a filled heart by visiting points on a grid and checking whether each point belongs to a heart-shaped region. Nested for loops produce the rows and columns, while a mathematical condition selects the positions that contain stars.
Source code
Save this example as HeartPatternExample.java. It uses a Java 25 compact source file with void main() and the IO console methods. Use JDK 25 or later; no preview flags or external libraries are required.
void main() {
int rows = 20;
int columns = 40;
for (int row = 0; row <= rows; row++) {
double y = 1.5 - 3.0 * row / rows;
for (int column = 0; column <= columns; column++) {
double x = -1.5 + 3.0 * column / columns;
double a = x * x + y * y - 1;
if (a * a * a - x * x * y * y * y <= 0) {
IO.print("*");
} else {
IO.print(" ");
}
}
IO.println();
}
}
Explanation
The pattern uses the heart-shaped region defined by this inequality:
(x² + y² - 1)³ - x²y³ <= 0
You do not need to derive the formula to practice the loops. Treat it as a membership test: a point that satisfies the condition gets a star; any other point gets a space. Unlike a circle, this region has two rounded lobes at the top and a point at the bottom.
The outer loop selects the row and calculates its vertical coordinate, y. It starts at 1.5 and moves downward to -1.5. Console output grows downward, so decreasing y keeps the heart upright.
The inner loop visits each column and calculates its horizontal coordinate, x, from -1.5 to 1.5. The constants 3.0 represent the width and height of this coordinate range. Using 3.0 makes the arithmetic floating-point rather than integer division, allowing neighboring positions to have fractional coordinates.
The variables rows and columns specify the number of intervals across the coordinate range. Because both loops include their final index with <=, the program prints rows + 1 lines with columns + 1 character positions per line. The example therefore samples a grid of 21 rows and 41 columns.
For each position, a stores x * x + y * y - 1. The condition then multiplies the values to calculate the powers in the inequality. In Java, ^ is an exclusive-OR operator, not exponentiation, so it would not express these powers.
IO.print("*") and IO.print(" ") each occupy one character position. After the inner loop finishes, IO.println() starts the next row. The IO class belongs to java.lang, so no explicit import is needed.
There are about twice as many horizontal intervals as vertical intervals. This helps balance the proportions in terminal fonts whose characters are taller than they are wide. The result remains a stepped approximation, and its appearance depends on the font. Rows outside the heart contain only spaces and appear blank.
Run the example
With JDK 25 or later installed, open a terminal in the directory containing the source file and run it directly:
java HeartPatternExample.java
You can also compile the source file explicitly and then run the compiled program:
javac HeartPatternExample.java
java HeartPatternExample
Output
****** ******
************ ************
*****************************
*****************************
*******************************
*******************************
*****************************
*****************************
***************************
*************************
*********************
*****************
*************
*********
***
Display the output in a monospaced font to preserve alignment. Trailing spaces are omitted from the output shown here; leading spaces and blank rows are preserved.
Try it yourself
Change rows to 30 and columns to 60. The coordinate range stays the same, but the denser grid gives the heart more detail. Keep both values positive to sample the full range correctly.
Replace IO.print("*") with IO.print("#") to change the drawing character without changing the shape.
Finally, change the condition to x * x + y * y <= 1. The same loops now draw a filled circle in the sampled coordinate range. Compare this coordinate-based approach with How do I print a circle pattern using loops in Java?.
