ANSI escape codes in Java
Java string literals have no \e escape — write \033 (octal) or \u001b (Unicode escape). System.out.println("\033[31mred\033[0m") is byte-clean on every JVM. On Windows, Conhost since Windows 10 1709 parses VT natively, but for compatibility with older Conhost (and to handle the Maven / Gradle / IntelliJ output-wrapping that often strips ANSI), call AnsiConsole.systemInstall() from Jansi — it swaps System.out and System.err with wrappers that translate ANSI to Win32 console-API calls on legacy hosts and pass through on modern ones.
For more than ad-hoc print statements: JLine 3 is Java's readline (raw mode, line editing, history, completion, masked password, terminal capability detection) — the foundation under Spring Shell, JBang, Groovy's groovysh, and OpenJDK's jshell. picocli annotation-drives CLI arg parsing with ANSI-aware help formatters via Help.Ansi.AUTO (honours NO_COLOR and isatty). For full-screen TUIs reach for Lanterna — a Swing-style hierarchy of Window / Panel / Component rendered to ANSI.
Recommended libraries
- Jansi
The canonical Windows-VT shim + ANSI builder.
AnsiConsole.systemInstall()swapsSystem.out/System.errso escapes work on Conhost pre-1709 (translates ANSI → Win32 console API). Fluent builder:Ansi.ansi().fg(RED).bold().a("error:").reset().a(" perm denied"). The output layer under Maven, Gradle, Spring Boot dev tools. - JLine 3
Java's
readline— raw mode, line editing, history, tab completion, masked password, terminal capability detection. Foundation under Spring Shell, JBang, Groovygroovysh, OpenJDKjshell. Bridges to Jansi on Windows; uses native PTY (org.jline.terminal.impl.PosixSysTerminal) on POSIX. - picocli
Annotation-driven CLI framework —
@Command,@Option,@Parameters. Auto-generates ANSI-coloured--helpviaHelp.Ansi.AUTO(respectsNO_COLOR+isatty). Markup syntax in description strings:@|bold,fg(red) error|@,@|underline link|@. Integrates with Jansi for Windows VT. - Lanterna
Cross-platform full-screen TUI library — Swing-style hierarchy of
Window,Panel,Component,Label,Button,TextBoxrendered to ANSI. Supports keyboard and mouse, multi-window layouts, theming. Used by classic Java TUI projects (text-mode games, sysadmin dashboards).
Idiomatic patterns
public class Main {
public static void main(String[] args) {
// Java has no \e literal — use \033 (octal) or \u001b (Unicode).
System.out.println("\033[1;31merror:\033[0m permission denied");
System.out.println("\u001b[1;32mok:\u001b[0m all 142 tests passed");
}
}import org.fusesource.jansi.AnsiConsole;
import static org.fusesource.jansi.Ansi.*;
import static org.fusesource.jansi.Ansi.Color.*;
public class Main {
public static void main(String[] args) {
AnsiConsole.systemInstall(); // safe no-op on modern hosts
try {
System.out.println(ansi()
.fg(RED).bold().a("error:")
.reset().a(" permission denied"));
// Truecolor (Jansi 2.x):
System.out.println(ansi()
.fgRgb(0xcb, 0xa6, 0xf7).a("lavender truecolor").reset());
} finally {
AnsiConsole.systemUninstall();
}
}
}import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Help.Ansi;
@Command(
name = "deploy",
mixinStandardHelpOptions = true,
description = "@|bold,fg(blue) Deploy|@ a service to @|fg(yellow) production|@."
)
public class Deploy implements Runnable {
@Option(names = {"-r", "--region"},
description = "AWS region (@|fg(cyan) e.g. us-east-1|@)")
String region;
public void run() {
// Ansi.AUTO respects NO_COLOR + isatty automatically.
System.out.println(Ansi.AUTO.string(
"@|bold,fg(green) ok|@: deploying to " + region));
}
public static void main(String[] args) {
System.exit(new CommandLine(new Deploy()).execute(args));
}
}import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
public class Main {
public static void main(String[] args) throws Exception {
try (Terminal term = TerminalBuilder.builder()
.jansi(true) // bridge to Jansi on Windows
.system(true) // attach to controlling TTY
.build()) {
Terminal.SignalHandler prev = term.handle(Terminal.Signal.INT, sig -> System.exit(0));
term.enterRawMode();
term.writer().println("press any key (q to quit):");
term.writer().flush();
int c;
while ((c = term.reader().read()) != 'q') {
term.writer().printf("0x%02x %n", c);
term.writer().flush();
}
}
}
}