dankamongmen / notcurses

blingful character graphics/TUI library. definitely not curses.

Home Page:https://nick-black.com/dankwiki/index.php/Notcurses

Notcurses: blingful TUIs and character graphics

What it is: a library facilitating complex TUIs on modern terminal emulators, supporting vivid colors, multimedia, threads, and Unicode to the maximum degree possible. Things can be done with Notcurses that simply can't be done with NCURSES. It is furthermore fast as shit. What it is not: a source-compatible X/Open Curses implementation, nor a replacement for NCURSES on existing systems.

setting the standard (hype video)

for more information, see dankwiki and the man pages. in addition, there is Doxygen output. there is a mailing list which can be reached via [email protected] i wrote a coherent guidebook, which is available for free download (or paperback purchase).

i've not yet added many documented examples, but src/poc/ and src/pocpp/ contain many small C and C++ programs respectively. notcurses-demo covers most of the functionality of Notcurses.

If you're running Notcurses applications in a Docker, please consult "Environment notes" below.

Packaging status

Linux FreeBSD Windows macOS Matrix Sponsor

Build Ubuntu macOS Windows

pypi_version crates.io


Notcurses abandons the X/Open Curses API bundled as part of the Single UNIX Specification. For some necessary background, consult Thomas E. Dickey's superb and authoritative NCURSES FAQ. As such, Notcurses is not a drop-in Curses replacement.

Wherever possible, Notcurses makes use of the Terminfo library shipped with NCURSES, benefiting greatly from its portability and thoroughness.

Notcurses opens up advanced functionality for the interactive user on workstations, phones, laptops, and tablets, possibly at the expense of e.g. some industrial and retail terminals. Fundamentally, Curses assumes the minimum and allows you (with effort) to step up, whereas Notcurses assumes the maximum and steps down (by itself) when necessary. The latter approach probably breaks on some older hardware, but the former approach results in new software looking like old hardware.

Why use this non-standard library?

  • Thread safety, and efficient use in parallel programs, has been a design consideration from the beginning.

  • A more orderly surface than that codified by X/Open: Exported identifiers are prefixed to avoid common namespace collisions. Where reasonable, static inline header-only code is used. This facilitates compiler optimizations, and reduces loader time. Notcurses can be built without its multimedia functionality, requiring a significantly lesser set of dependencies.

  • All APIs natively support the Universal Character Set (Unicode). The nccell API is based around Unicode's Extended Grapheme Cluster concept.

  • Visual features including images, fonts, video, high-contrast text, sprites, and transparent regions. All APIs natively support 24-bit color, quantized down as necessary for the terminal.

  • Portable support for bitmapped graphics, whether using Sixel, Kitty, the iTerm2 protocol, or even the Linux framebuffer console.

  • Support for unambiguous keyboard protocols.

  • "TUI mode" facilitates high-performance, non-scrolling, full-screen applications. "CLI mode" supports scrolling output for shell utilities, but with the full power of Notcurses.

  • It's Apache2-licensed in its entirety, as opposed to the drama in several acts that is the NCURSES license (the latter is summarized as "a restatement of MIT-X11").

Much of the above can be had with NCURSES, but they're not what NCURSES was designed for. On the other hand, if you're targeting industrial or critical applications, or wish to benefit from time-tested reliability and portability, you should by all means use that fine library.


Minimum versions generally indicate the oldest version I've tested with; it may well be possible to use still older versions. Let me know of any successes!

  • (build) CMake 3.14.0+ and a C11 compiler
  • (OPTIONAL) (OpenImageIO, testing, C++ bindings): A C++17 compiler
  • (build+runtime) From NCURSES: terminfo 6.1+
  • (build+runtime) GNU libunistring 0.9.10+
  • (OPTIONAL) (build+runtime) libgpm 1.20+
  • (OPTIONAL) (build+runtime) From QR-Code-generator: libqrcodegen 1.5.0+
  • (OPTIONAL) (build+runtime) From FFmpeg: libswscale 5.0+, libavformat 57.0+, libavutil 56.0+
  • (OPTIONAL) (build+runtime) OpenImageIO 2.15.0+, requires C++
  • (OPTIONAL) (testing) Doctest 2.3.5+
  • (OPTIONAL) (documentation) pandoc 1.19.2+
  • (OPTIONAL) (python bindings): Python 3.7+, CFFI 1.13.2+, pypandoc 1.5+
  • (OPTIONAL) (rust bindings): rust 1.47.0+, bindgen 0.55.1+, pkg-config 0.3.18+, cty 0.2.1+
  • (runtime) Linux 5.3+, FreeBSD 11+, DragonFly BSD 5.9+, Windows Vista+, or macOS 11.4+

Here's more information on building and installation.


If you wish to use a language other than C to work with Notcurses, numerous wrappers are available. Several are included in this repository, while others are external.

Language Lead(s) Repository
Ada Jeremy Grosser JeremyGrosser/notcursesada
C++ Marek Habersack, nick black internal
Python nick black internal
Python igo95862 internal
Rust José Luis Cruz dankamongmen/libnotcurses-sys
Zig Jakub Dundalek dundalek/notcurses-zig-example

Included tools

Eight binaries are installed as part of Notcurses:

  • ncls: an ls that displays multimedia in the terminal
  • ncneofetch: a neofetch ripoff
  • ncplayer: renders visual media (images/videos)
  • nctetris: a tetris clone
  • notcurses-demo: some demonstration code
  • notcurses-info: detect and print terminal capabilities/diagnostics
  • notcurses-input: decode and print keypresses
  • notcurses-tester: unit testing

To run notcurses-demo from a checkout, provide the data directory via the -p argument. Demos requiring data files will otherwise abort. The base delay used in notcurses-demo can be changed with -d, accepting a floating-point multiplier. Values less than 1 will speed up the demo, while values greater than 1 will slow it down.

notcurses-tester likewise requires that data, populated with the necessary data files, be specified with -p. It can be run by itself, or via make test.


With -DUSE_PANDOC=on (the default), a full set of man pages and XHTML will be built from doc/man. The following Markdown documentation is included directly:

If you (understandably) want to avoid the large Pandoc stack, but still enjoy manual page goodness, I publish a tarball with generated man/XHTML along with each release. Download it, and install the contents as you deem fit.

Environment notes

  • If your TERM variable is wrong, or that terminfo definition is out-of-date, you're going to have a very bad time. Use only TERM values appropriate for your terminal. If this variable is undefined, or Notcurses can't load the specified Terminfo entry, it will refuse to start, and you will not be going to space today.

  • Notcurses queries the terminal on startup, enabling some advanced features based on the determined terminal (and even version). Basic capabilities, however, are taken from Terminfo. So if you have, say, Kitty, but TERM=vt100, you're going to be able to draw RGBA bitmap graphics (despite such things being but a dream for a VT100), but unable to use the alternate screen (despite it being supported by every Kitty version). So TERM and an up-to-date Terminfo database remain important.

  • Ensure your LANG environment variable is set to a UTF8-encoded locale, and that this locale has been generated. This usually means "[language]_[Countrycode].UTF-8", i.e. en_US.UTF-8. The first part (en_US) ought exist as a directory or symlink in /usr/share/locales. This usually requires editing /etc/locale.gen and running locale-gen. On Debian systems, this can be accomplished with dpkg-reconfigure locales, and enabling the desired locale. The default locale is stored somewhere like /etc/default/locale.

  • If your terminal has an option about default interpretation of "ambiguous-width characters" (this is actually a technical term from Unicode), ensure it is set to Wide, not narrow (if that doesn't work, ensure it is set to Narrow, heh).

  • If your terminal supports 3x8bit RGB color via setaf and setbf (most modern terminals), but exports neither the RGB nor Tc terminfo capability, you can export the COLORTERM environment variable as truecolor or 24bit. Note that some terminals accept a 24-bit specification, but map it down to fewer colors. RGB is unconditionally enabled whenever most modern terminals are identified.


Glyph width, and indeed whether a glyph can be displayed at all, is dependent in part on the font configuration. Ideally, your font configuration has a glyph for every Unicode EGC, and each glyph's width matches up with the C library's wcswidth() result for the EGC. If this is not the case, you'll likely get blanks or � (U+FFFD, REPLACEMENT CHARACTER) for missing characters, and subsequent characters on the line may be misplaced.

It is worth knowing that several terminals draw the block characters directly, rather than loading them from a font. This is generally desirable. Quadrants and sextants are not the place to demonstrate your design virtuosity. To inspect your environment's rendering of drawing characters, run notcurses-info. The desired output ought look something like this:

notcurses-info can be used to check Unicode drawing


If things break or seem otherwise lackluster, please consult the Environment Notes section! You need to have a correct TERM and LANG definition, and probably want COLORTERM.

The demo fails in the middle of intro. Check that your TERM value is correct for your terminal. intro does a palette fade, which is prone to breaking under incorrect TERM values. If you're not using xterm, your TERM should not be xterm!
Can I write a CLI program (scrolling, fits in with the shell, etc.) with Notcurses? Yes! Use the flags NCOPTION_NO_ALTERNATE_SCREEN, NCOPTION_NO_CLEAR_BITMAPS, and NCOPTION_PRESERVE_CURSOR, and call `ncplane_set_scrolling()` on the standard plane. You still must explicitly render.
Can I have Notcurses without this huge multimedia stack? Again yes! Build with -DUSE_MULTIMEDIA=none.
Can I build this individual Notcurses program without aforementioned multimedia stack? Almost unbelievably, yes! Use notcurses_core_init() or ncdirect_core_init() in place of notcurses_init()/ ncdirect_init(), and link with -lnotcurses-core. Your application will likely start a few milliseconds faster; more importantly, it will link against minimal Notcurses installations.
Does it work with hardware terminals? With the correct `TERM` value, many hardware terminals are supported. The VT100 is sadly unsupported due to its extensive need for delays. In general, if the terminfo database entry indicates mandatory delays, Notcurses will not currently support that terminal properly. It's known that Notcurses can drive the VT320 and VT340, including Sixel graphics on the latter.
What happens if I try blitting bitmap graphics on a terminal which doesn't support them? Notcurses will not make use of bitmap protocols unless the terminal positively indicates support for them, even if `NCBLIT_PIXEL` has been requested. Likewise, sextants (`NCBLIT_3x2`) won't be used without Unicode 13 support, etc. `ncvisual_render()` will use the best blitter available, unless `NCVISUAL_OPTION_NODEGRADE` is provided (in which case it will fail).
Notcurses looks like absolute crap in screen. screen doesn't support RGB colors (at least as of 4.08.00); if you have COLORTERM defined, you'll have a bad time. If you have a screen that was compiled with --enable-colors256, try exporting TERM=screen-256color as opposed to TERM=screen.
Notcurses looks like absolute crap in mosh. Yeah it sure does. I'm not yet sure what's up.
Notcurses looks like absolute crap in Windows Terminal. Go to [Language Settings](ms-settings:regionlanguage), click "Administrative language settings", click "Change system locale", and check the "Beta: Use Unicode UTF-8 for worldwide language support" option. Restart the computer. That ought help a little bit. Ensure your code page is 65001 with `chcp 65001`. Try playing with fonts.
Why didn't you just render everything to Sixel? That's not a TUI; it's a slow and inflexible GUI. Many terminal emulators don't support Sixel. Sixel doesn't work well with mouse selection. Sixel has a limited color palette. With that said, both Sixel and the Kitty bitmap protocol are well-supported.
I'm not seeing NCKEY_RESIZE until I press some other key. You've almost certainly failed to mask SIGWINCH in some thread, and that thread is receiving the signal instead of the thread which called notcurses_getc_blocking(). As a result, the poll() is not interrupted. Call pthread_sigmask() before spawning any threads.
Using the C++ wrapper, how can I ensure that the NotCurses destructor is run when I return from main()? As noted in the C++ FAQ, wrap it in an artificial scope (this assumes your NotCurses is scoped to main()).
How do I hide a plane I want to make visible later? In order of least to most performant: move it offscreen using ncplane_move_yx(), move it underneath an opaque plane with ncplane_move_below(), or move it off-pile with ncplane_reparent().
Why isn't there an ncplane_box_yx()? Do you hate orthogonality, you dullard? ncplane_box() and friends already have far too many arguments, you monster.
Why doesn't Notcurses support 10- or 16-bit color? Notcurses supports 24 bits of color, spread across three eight-bit channels. You presumably mean 10-bit-per-channel color. I needed those six bits for other things. When terminals support it, Notcurses might support it.
The name is dumb. That's not a question?
I'm not finding qrcodegen on BSD, despite having installed graphics/qr-code-generator. Try cmake -DCMAKE_REQUIRED_INCLUDES=/usr/local/include. This is passed by bsd.port.mk.
Do you support musl? I try to! You'll need at least 1.20.
I only seem to blit in ASCII, and/or can't emit Unicode beyond ASCII in general. Your LANG environment variable is underdefined or incorrectly defined, or the necessary locale is not present on your machine (it is also possible that you explicitly supplied NCOPTION_INHIBIT_SETLOCALE, but never called setlocale(3), in which case don't do that).
I pretty much always need an ncplane when using a nccell. Why doesn't the latter hold a pointer to the former? Besides the massive redundancy this would entail, nccell needs to remain as small as possible, and you almost always have the ncplane handy if you've got a reference to a valid nccell anyway.
I ran notcurses-demo, but my table numbers don't match the Notcurses banner numbers, you charlatan. notcurses-demo renders several frames beyond the actual demos.
When my program exits, I don't have a cursor, or text is invisible, or colors are weird, ad nauseam. Ensure you're calling notcurses_stop()/ncdirect_stop() on all exit paths, including fatal signals (note that, by default, Notcurses installs handlers for most fatal signals to do exactly this).
How can I use Direct Mode in conjunction with libreadline? You can't anymore (you could up until 2.4.1, but the new input system is fundamentally incompatible with it). `ncdirect_readline()` still exists, though, and now actually works even without libreadline, though it is of course not exactly libreadline. In any case, you'd probably be better off using CLI mode with a ncreader.
So is Direct Mode deprecated or what? It is not currently deprecated, and definitely receives bugfixes. You are probably better served using CLI mode (see above), which came about somewhat late in Notcurses development (the 2.3.x series), but is superior to Direct Mode in pretty much every way. The only reason to use Direct Mode is if you're going to have other programs junking up your display.
Direct Mode sounds fast! Since it's, like, direct. Direct mode is substantially slower than rendered mode. Rendered mode assumes it knows what's on the screen, and uses this information to generate optimized sequences of escapes and glyphs. Direct mode writes everything it's told to write. It is furthermore far less capable—all widgets etc. are available only to rendered mode, and will definitely not be extended to Direct Mode.
Will there ever be Java wrappers? I should hope not. If you want a Java solution, try Autumn Lamonte's Jexer.
Given that the glyph channel is initialized as transparent for a plane, shouldn't the foreground and background be initialized as transparent, also? Probably (they are instead by default initialized to opaque). This would change some of the most longstanding behavior of Notcurses, though, so it isn't happening.
Why does my right-to-left text appear left-to-right? Notcurses doesn't honor the BiDi state machine, and in fact forces left-to-right with BiDi codes. Likewise, ultra-wide glyphs will have interesting effects. ﷽!
I get linker errors when statically linking. Are you linking all necessary libraries? Use pkg-config --static --libs notcurses (or --libs notcurses-core) to discover them.
Can I avoid manually exporting COLORTERM=24bit everywhere? Sure. Add SendEnv COLORTERM to .ssh/config, and AcceptEnv COLORTERM to sshd_config on the remote server. Yes, this will probably require root on the remote server. Don't blame me, man; I didn't do it.
How about *arbitrary image manipulation here* functionality? I'm not going to beat ImageMagick et al. on image manipulation, but you can load an ncvisual from RGBA memory using ncvisual_from_rgba().
My program locks up during initialization. Notcurses interrogates the terminal. If the terminal doesn't reply to standard interrogations, file a Notcurses bug, send upstream a patch, or use a different terminal. No known terminal emulators exhibit this behavior.
Why no NCSTYLE_REVERSE? It would consume a precious bit. You can use ncchannels_reverse() to correctly invert fore- and background colors.
How do I mix Rendered and Direct mode? You really don't want to. You can stream a subprocess to a plane with the ncsubproc widget.
How can I clear the screen on startup in Rendered mode when not using the alternate screen? Call notcurses_refresh() after notcurses_init() returns successfully.
Why do the stats show more Linux framebuffer bitmap bytes written than total bytes written to the terminal? Linux framebuffer graphics aren't implemented via terminal writes, but instead writes directly into a memory map.

“Our fine arts were developed, their types and uses were established, in times

very different from the present, by men whose power of action upon things was insignificant in comparison with ours. But the amazing growth of our techniques, the adaptability and precision they have attained, the ideas and habits they are creating, make it a certainty that profound changes are impending in the ancient craft of the Beautiful.” —Paul Valéry


blingful character graphics/TUI library. definitely not curses.




Language:C 82.3%Language:C++ 14.9%Language:Python 1.8%Language:CMake 0.7%Language:Shell 0.3%