Skip to content

Both.org

News, Opinion, Tutorials, and Community for Linux Users and SysAdmins

Primary Menu
  • About Us
  • Computers 101
    • Hardware 101
    • Operating Systems 101
  • End of 10 Events
    • Wake Forest, NC, — 2025-09-20
  • Linux
    • Why I use Linux
    • The real reason we use Linux
  • My Linux Books
    • systemd for Linux SysAdmins
    • Using and Administering Linux – Zero to SysAdmin: 2nd Edition
    • The Linux Philosophy for SysAdmins
    • Linux for Small Business Owners
    • Errata
      • Errata for The Linux Philosophy for SysAdmins
      • Errata for Using and Administering Linux — 1st Edition
      • Errata for Using and Administering Linux — 2nd Edition
  • Open Source Resources
    • What is Open Source?
    • What is Linux?
    • What is Open Source Software?
    • The Open Source Way
  • Write for us
    • Submission and Style guide
    • Advertising statement
  • Downloads
  • Home
  • Holiday snow on your terminal
  • Code

Holiday snow on your terminal

Jim Hall December 19, 2023 7 minutes read
code_development_programming

I live in Minnesota and we haven’t gotten a lot of snow yet, so I figured I’d add a little “holiday cheer” by writing a program to add “gently falling snow” to my terminal instead. This program is also a good demonstration for how to update your terminal in character mode using the ncurses library.

Initialize the terminal

Before you can use any terminal-drawing routines from ncurses, your program will need to initialize the terminal. Most programs will use initscr() to set up the screen and set certain global values such as COLS for the number of columns and LINES for the number of lines, followed by cbreak() to disable buffering, and noecho() so characters typed by the user aren’t “echoed” on screen. These functions are defined in curses.h.

Some programs might also call intrflush(stdscr, FALSE) to disable “flushing” the screen output if the user presses an interrupt key. You can also use curs_set to control how the cursor should be displayed; use curs_set(0) to completely hide the cursor.

When the program is done, you must call endwin() to exit ncurses. Programs that simply do a thing and then end might wait for the user to press a key before returning to the command line. We can use the getch() function to return a single keystroke from the keyboard, which effectively waits for the user before taking action.

The basic outline for a program that uses ncurses to clear the screen and wait for the user to press a key looks like this:

#include <curses.h>

int
main()
{
  initscr();
  cbreak();
  noecho();

  intrflush(stdscr, FALSE);
  curs_set(0);

  getch();

  endwin();
  return 0;
}

Set up the screen

To write our “falling snow” program, we’ll first need to establish the “ground” on the terminal. The hline(ch,n) function in ncurses will draw a horizontal line of n characters at the current position on the screen. To draw the line at a specific location, we can use the move(y,x) function to set the position before calling hline(ch,n) to add the characters, or we can call mvhline(y,x,ch,n) to do both at the same time.

If we want to add text, such as a “Happy holidays” message, the addstr(s) function will add a string at the current screen position. We can use the similarly named mvaddstr(y,x,s) to add a string at a specific coordinate on the terminal.

Screen coordinates count from zero, so the top-left coordinate on the screen is 0,0 and the bottom-left position on the screen is LINES-1,0. We can use these coordinates to add a horizontal line and some text to the bottom of the screen:

  mvhline(LINES - 2, 0, ACS_HLINE, COLS);
  mvaddstr(LINES - 1, 0, "Let it snow!");
  refresh();

The refresh() function instructs ncurses to update the screen with the new text.

Gently falling snowflakes

To simulate the effect of a “gently falling snowflake” from the top of the screen, we’ll need to write a function called drop that draws an asterisk to represent a snowflake. Let’s define the function as drop(int row, int col) to start the snowflake at a specific coordinate. The function can continue to move the snowflake down the screen until it reaches some non-blank character, such as the border we drew with mvhline.

The addch(ch) function from ncurses adds a character at the current position on the screen. Like other functions that draw to the screen, we can use the mv variant of this function to first move to a specific location before adding the character. For example, to add an asterisk at row 5 and column 3, you would use mvaddch(5,3,'*').

We can use mvaddch again and again to reposition an asterisk. By adding a slight pause between iterations, we can create the effect of a single snowflake falling to the ground:

void
drop(int row, int col)
{
  /* drop a "snowflake" starting at row,col .. keep going until we run out
     of empty space */

  int y = row;

  mvaddch(y, col, '*');

  while (mvinch(y + 1, col) == ' ') {
    usleep(10000);                     /* 10000 microsecs = .01 secs */
    mvaddch(y, col, ' ');
    mvaddch(++y, col, '*');
    refresh();
  }
}

The usleep function is defined in unistd.h.

Randomize the snow

With our drop function to simulate a single snowflake, we can draw lots of snow falling one by one by using the same function across every column on the screen. But snow doesn’t fall from left to right; it’s random.

A simple way to randomize how the snow falls on our screen is to generate an array of all the column coordinates, then “shuffle” the array so all the coordinates appear in random order. Initializing the array is easy; that’s just assigning a number to each member in the array.

One way to randomize the array is to swap each element with another element chosen at random. The most effective way to do this is by “walking” backwards through the array and choosing another random member that comes before it:

void
randfill(int *array, int nsize)
{
  srand(time(NULL));                   /* pseudorandom but works anywhere */

  for (int i = 0; i < nsize; i++) {
    array[i] = i;
  }

  for (int i = nsize - 1; i > 0; i--) {
    swap(array, i, rand() % (i + 1));  /* pick a random number 0..i */
  }
}

The Linux kernel provides a getrandom system call that generates a series of random bytes, which effectively produces a random number. To allow you to compile this program on other systems, I’ve used the rand function from the C standard library. This produces pseudorandom values, which aren’t completely random, but good enough for a “falling snow” program—and it works everywhere.

I could have written the “swap” feature inside the second loop, but I thought the code would be a little more readable if I moved that into a separate function called swap that swaps two elements of an array:

void
swap(int *ary, int i, int j)
{
  /* swap two elements i and j */
  int tmp = ary[i];
  ary[i] = ary[j];
  ary[j] = tmp;
}

Let it snow

With these functions, we can now update the main program to draw the “ground” and some text on the terminal, then simulate gently falling snow. So the program doesn’t take forever to run, this only fills up the bottom five lines with “snow.” Or if you’ve defined a really small terminal window that’s less than ten lines, the program fills up about half of the screen.

#include <stdlib.h>
#include <time.h>

#include <curses.h>
#include <unistd.h>

#include <sys/param.h>

void
swap(int *ary, int i, int j)
{
  /* swap two elements i and j */
  int tmp = ary[i];
  ary[i] = ary[j];
  ary[j] = tmp;
}

void
randfill(int *array, int nsize)
{
  srand(time(NULL));                   /* pseudorandom but works anywhere */

  for (int i = 0; i < nsize; i++) {
    array[i] = i;
  }

  for (int i = nsize - 1; i > 0; i--) {
    swap(array, i, rand() % (i + 1));  /* pick a random number 0..i */
  }
}

void
drop(int row, int col)
{
  /* drop a "snowflake" starting at row,col .. keep going until we run out
     of empty space */

  int y = row;

  mvaddch(y, col, '*');

  while (mvinch(y + 1, col) == ' ') {
    usleep(10000);                     /* 10000 microsecs = .01 secs */
    mvaddch(y, col, ' ');
    mvaddch(++y, col, '*');
    refresh();
  }
}

int
main()
{
  int *colx;

  initscr();
  cbreak();
  noecho();

  intrflush(stdscr, FALSE);
  curs_set(0);                         /* hide cursor */

  /* allocate the x coords */

  colx = calloc(sizeof(int), COLS);

  if (colx == NULL) {                  /* error */
    endwin();
    return 1;
  }

  randfill(colx, COLS);

  /* set up the screen */

  mvhline(LINES - 2, 0, ACS_HLINE, COLS);
  mvaddstr(LINES - 1, 0, "Let it snow!");
  refresh();

  /* snow */

  for (int row = 0; row < MIN(5, LINES / 2); row++) {
    for (int c = 0; c < COLS; c++) {
      drop(0, colx[c]);
    }
  }

  /* done */

  mvaddstr(LINES - 1, 0, "press any key to quit . . .");
  getch();

  endwin();

  free(colx);
  return 0;
}

Save the program as snow.c, then compile and run it to simulate gently falling snow on your terminal:

$ gcc -Wall -o snow snow.c -lncurses
Screenshot of a program showing gently falling snow on a Linux terminal
Screenshot of our program showing gently falling snow on a Linux terminal.

Just in case you want to play with this little program without compiling it yourself, here’s a link to snow. Download it to your system and change the permissions to 555. If it’s in your path, launch it from your terminal session by typing snow. If it’s not in the path, prepend the path to the program name. Use ./snow if it’s in the current directory.

Tags: Fun Linux ncurses

Post navigation

Previous: Interview Hack: AI saves the day(and ears)
Next: How I troubleshoot swappiness and startup time on Linux

Related Stories

code_development_programming
  • Algol 68
  • Code
  • Interview
  • Programming

Interview with Marcel van der Veer

Chris Hermansen September 25, 2025
programming_keyboard_coding
  • Algol 68
  • Code
  • Hacking
  • History
  • Linux
  • Programming

Exploring GNU Algol 68: Formatting numbers as strings for output

Chris Hermansen September 3, 2025
code_development_programming
  • Algol 68
  • Code
  • Linux
  • Programming

Exploring GNU Algol 68

Chris Hermansen August 28, 2025

System upgrades completed

Tools illustrationUpgrades to Fedora 44 for the Both.org firewall and server completed Sunday afternoon at about 14:39, EDT.

There was a problem with SSL certificates that prevented HTTPD, the website server from starting after the upgrade. It only took a couple minutes to fix and all now seems to be running smoothly.

Thanks for your patience.

Random Quote

Build a system that even a fool can use and only a fool will want to use it.

— Shaw’s principle

Why I’ve Never Used Windows

On February 12 I gave a presentation at the Triangle Linux Users Group (TriLUG) about why I use Linux and why I’ve never used Windows.

Here’s the link to the video: https://www.youtube.com/live/uCK_haOXPFM 

Why there’s no such thing as AI

Last October at All Things Open (ATO) I was interviewed by Jason Hibbits of We Love Open Source. It’s posted in the article “Why today’s AI isn’t intelligent (yet)“.

Technically We Write — Our Partner Site

Our partner site, Technically We Write, has published a number of articles from several contributors to Both.org. Check them out.

Technically We Write is a community of technical writers, technical editors, copyeditors, web content writers, and all other roles in technical communication.

Subscribe to Both.org

To comment on articles, you must have an account.

Send your desired user ID, first and last name, and an email address for login (this must be the same email address used to register) to subscribe@both.org with “Subscribe” as the subject line.

You’ll receive a confirmation of your subscription with your initial password as soon as we are able to process it.

Administration

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

License and AI Statements

Both.org aims to publish everything under a Creative Commons Attribution ShareAlike license. Some items may be published under a different license. You are responsible to verify permissions before reusing content from this website.

The opinions expressed are those of the individual authors, not Both.org.

You may not use this content to train AI.

 

Advertising Statement

Both.org does not sell advertising on this website.


Advertising may keep most websites running—but at Both.org, we’re committed to keeping our corner of the web ad-free. Both.org does not sell advertising on the website. Nor do we offer sponsored articles at this time. We’ll update this page if our position on sponsorships changes.

We want to be open about how the website is funded. Both.org is supported entirely by David Both and a few other dedicated individuals.

 

 

Copyright © All rights reserved. | MoreNews by AF themes.