Skip to main content

A creative coding language based on Processing and Python for creating interactive character terminal art.

Project description

Charming: Character Terminal Art Programing

Overview

Charming is a creative coding language designed for interactive character terminal art.

It currently written in Python and provides Processing-like APIs, which aims to help artists, designers, educators, beginners, and anyone else to easily create ascii art animation, character-style generative art, terminal game application and expressive data visulization.

Documentation | Examples | Tests Samples

Why is it

Renaissance of ASCII Art

With Open Frameworks, Processing, P5js getting more and more popular, people pay more attention on using computer and coding to make exquisite and complex artworks or infomation graphics nowadays. Here are some examples created by me.

It seems like we gradully forget an old and pure form of art which was born with the computer and programer -- ASCII Art, pictures pieced together from the 95 printable (from a total of 128) characters defined by the ASCII Standard. There are some examples from textfancy.

Back in 1970s and early 1980s, computers were not as accessible as now, neverthless to create sophisticated visual effects. But at that time, ASCII Art had showed up and somehow meant to belong to the programmers of that genertion who mostly programmed in a text-based terminal day and night, so ASCII Art may be the best way to show the original charm and romance of computers and of programmers.

For example, it will be very romantic if you using snake-eating to write a poem. Here is a example created by Charming that you move the snake, eat the food and finally you get the poem: This Is Just To Say.

charm

So we have to make the ASCII Art prosperous again.

Powerful and intuitive

Charming is not the first tool which can make ANSCII Art and will certainly not be the last one , but it is more powerful and intuitive than most of exsiting tools.

On the one hand, we are not in 1970s or early 1980s after all, it will be very awkward if we limit ASCII Art to ASCII code and images.

With the appearence of Unicode (including CJK characters and Emoji) and the concept of Generative Art, it is time for us to expand the boundaries of ASCII Art to Character Terminal Art, which means using characters(not just ASCII characters) and algorithms to create awsome artworks in the terminal.

One of the biggest benefit of characters is that we can encoding more information for our artworks besides color, which means we can easily express more than traditional styles.

For instance, the main task for data visulization is to convey information, and colors do a great job on it. But if we extend the concept of color to include character, the character definitely can give extra information which will make the visulization more expressive and impressive.

There is a bar chart for mock data about covid-19 virus created by Charming. Instead of only using green for the curve, red for the confirm, gray for the dead, it also use 🌈 to express happiness and hopeness, use 🦠 to strengthen the warning, and use ☠️ to show sadness and fear. They are indeed make this chart more vivid and unforgettable.

charm

Charming is born for Character Terminal Art, so only a small part of APIs are related to ASCII Art. Its power focus more on drawing some basic shapes such as line, rectangle, circle, bezier curve, custom shape, etc. or apply some transformations including rotate, translate, scale and shear.

On the other hand, being powerful usually means complex usage and steep leanrning curve because of its flexibility.

But thanks to Processing and P5js, they have already introduced a intutive way of coding to the public. Charming makes full use of that and provide similar APIs with them, so you can code in Charming just like code in Processnig or P5js if you are familiar with them.

Have fun and to be present

With the help of artificial intelligence, computer science and software engineering gain more and more attention and so does Python. A large number of people choose to learn Python to make a living, but programming and Python are far more than that.

Just like most of us do not play basketball for career purpose, we should consider programming or programming in Python as a new kind of hobby. Because life can be without machine learning, web crawler or automated operations, but it can not be without creating and sharing things to have fun and to be present.

With the help of Charming, you are able to print something really awsome to the terminal when you are learning Python instead of just print something boring and stupid log information.

In that case, I hope not only does Charming make you love programming for fun or show a magic world to you, but also make this journey relaxing and interesting.

charm

Get started

Charming currently only supports MacOS, though it should also work for any other platform that provides a working curses implementation. It soon will support Windows and running in modern browsers.

It is very easy to get started with Charming as long as you install Python3.6+ already.

First of all, open an terminal and excute the command as below.

pip3 install charming

Then, create a file named sketch.py and copy the code to it and save.

''' sketch.py '''

import charming as app

# draw a rect
app.full_screen()
app.rect(0, 0, 10, 10)

# run the sketch
app.run()

Finllay, open an terminal and run command as below. Congratulations to you if you get a simple rectangle in your terminal.

python3 sketch.py

get started

Features

To better show the features of Charming, the following introduction will take Processing as a reference and comparision.

Structure

Like there are static mode for static effects and active mode for dynamic effects in Processing, you can also use them in Charming but with a little difference.

Static Mode

In Processing, you needn't import APIs or call an extra method to run the sketch, but you need import APIs at first and call an extra method run to run the sketch in Charming.

/* processing code: static mode */

size(900, 300);
rect(0, 0, 100, 150);

processnig structure

''' charming code: static mode '''

# import APIs
import charming as app

app.full_screen()
app.rect(0, 0, 10, 10)

# run sketch
app.run()

processnig static structure

Active Mode

Processing will automatically run the setup and draw functions you defined, but Charming will run them only they have been registered by specific dedecorators.

/* processing code: active mode */

void setup() {
    size(900, 300);
}

int x = 0;

void draw() {
    background(0);
    x += 1;
    rect(x, 0, 100, 150);
}

processnig active structure

''' charming code: active mode '''

import charming as app

@app.setup
def setup():
    app.full_screen()

x = 0

@app.draw
def draw():
    app.background(' ')
    global x
    x += 1
    app.rect(x, 0, 10, 10)

app.run()

active structure

Color

The biggest difference between Charming or Processing is the definition of color, which make Charming so unique to some extend.

In Processing, a color normally has three or four channels: (r, g, b) or (r,g, b, a) in RGB color mode and (h, s, b) or (h, s, b, a) in HSB mode, each channel is represented by a number.

/* processing color*/

size(900, 300);
stroke(255, 0, 0);
fill(255, 255, 0);
rect(0, 0, 100, 100);

processnig color

Basic Use

Colors are very different in Charming. In Charming, a color consists of three channels: (ch, fg, bg).

  • ch: character, ascii code or unicode (including cjk characters or emoji).
  • fg: foreground color, a number(0 ~ 255 by default) if the color mode is ANSI, a tuple with length equaling to 1or 3 if the color mode is HSB or RGB.
  • bg: background color, a number(0 ~ 255 by default) if the color mode is ANSI, a tuple with length equaling to 1 or 3 if the color mode is HSB or RGB.
''' charming color: ANSI '''

import charming as app

app.full_screen()
app.no_cursor()
app.stroke('O', app.GREEN, app.MAGENTA)
app.fill('X', 93, 220)
app.rect(0, 0, 10, 10)

app.run()

ansi color

RGB Color Mode

As a result of the terminal limitation, there are only 256 ANSI colors avilable for terminal which are respresent by 0 ~ 255. Aslo, you can use RED, BLACK, CYAN, YELLOW, GREEN, BLUE, WHITE, MAGENTA directly.

But in Charming, you can still use a tuple to represent the fg and bg of a color if you change the color mode to RGB or HSB, and they are convert to the closet color among the ANSI colors.

''' charming color: RBG '''

import charming as app

# Set color mode to RGB
app.color_mode(app.RGB)
app.no_cursor()
app.full_screen()

for i in range(7):
    v = app.map(i, 0, 7, 0, 255)
    app.stroke_weight(1)

    # r
    app.stroke('@', (v, 0, 0), (v, 0, 0))
    app.point(i * 4, 5)

    # g
    app.stroke('@', (0, v, 0), (0, v, 0))
    app.point(i * 4, 10)

    # b
    app.stroke('@', (0, 0, v), (0, 0, v))
    app.point(i * 4, 15)

app.run()

rgb color

HSB Color Mode

''' charming color: HSB '''

import charming as app

# Set color mode to HSB
app.color_mode(app.HSB)
app.full_screen()
app.no_cursor()

for h in range(360):
    x = h % 30
    y = h // 30
    app.stroke('@', (h, 100, 100), (h, 100, 100))
    app.point(x, y)

app.run()

hsb color

Double mode

You may already found something not workd as expexted.

  • Squares and ellipses drawn on the terminal are deformative.
  • Color with unicode character cause some confusing result.

The first one is that each cell of the terminal are not square which means that its width doesn't equal to its height. And the second one is that normally ascii codes need one cell to display and unicodes need two cell to display.

In order to solve both of them, you can change the renderer of Chamring to DOUBLE mode when you call size() or full_screen(). In that mode, Charming will use two cells to display both of ascii codes and unicodes.

''' charming: double mode'''
import charming as app

app.full_screen(app.DOUBLE)

app.stroke('@', fg=app.GREEN)
app.fill('🚀', bg=app.BLUE)
app.square(0, 0, 10)

app.fill('0', bg=app.BLUE)
app.circle(20, 10, 10)
app.run()

double mode

There is something important you should pay attention to. Charming can't always get the right width of unicodes, so if you find something wrong when use unicodes, you call declare the width of that unicode directly.

''' charming: double mode'''

app.full_screen(app.DOUBLE)

app.stroke('@', fg=app.GREEN)
# use a tuple to declare the width of that unicode
app.fill(('⏰', 2), bg=app.BLUE)
app.square(0, 0, 10)

app.run()

Typography

It is as easy as Processing to display text in Charming, but only with three level of text size.

  • NORMAL: Draw some basic words or ANSII Art to the terminal.
  • BIG: Easily convert some normal words to ANSII Art words.
  • LARGE: Convert some ANSII Art words to a bigger ANSII Art words.

Normal Size

''' charming text: normal size '''

import charming as app

app.full_screen()

head = r'''
         .-"""-.
        /       \
        \       /
 .-"""-.-`.-.-.<  _
/      _,-\ ()()_/:)
\     / ,  `     `|
 '-..-| \-.,___,  /
       \ `-.__/  /
        `-.__.-'`
'''

# draw basic texts
app.text('charming', 0, 0)

# draw some ascii art
app.text(head, 0, 5)

app.run()

normal text

Big Size

''' charming text: big size '''

import charming as app

app.full_screen()

# convert basic text to ascii text
app.text_size(app.BIG)
app.text('charming', 0, 0)

# support many fonts
fonts = app.get_font_list()
app.text_font(fonts[2])
app.text('charming', 0, 10)

app.run()

big text

Large Size

''' charming text: normal size '''

import charming as app

app.full_screen()
app.no_cursor()

head = r'''
         .-"""-.
        /       \
        \       /
 .-"""-.-`.-.-.<  _
/      _,-\ ()()_/:)
\     / ,  `     `|
 '-..-| \-.,___,  /
       \ `-.__/  /
        `-.__.-'`
'''

# convert ascii text to a lager ascii text
app.text_size(app.LARGE)
app.text(head, 0, 0)

app.run()

large text

Image

In Charming, it is possible for you to draw a image to terminal as simple as Processing, but only a subset of raw pixels will be displayed.

''' charming: image '''

import charming as app

app.no_cursor()
app.full_screen()
img = app.load_image('avatar.png')
app.image(img, 0, 0, 60, 30)

app.run()

image

Events

Events means you can bring life to your artworks, because you can interact with it and it will give you feedback.

Simliar to setup and draw function, you must regerster event listeners (mouse_pressed, key_pressed, etc.) by decorators.

Besides mouse events and keyboard events, Charming provide a unique type of events which deeply embeded in terminal: cursor events, which will be triggered if you type or press the UP/Right/DOWN/LEFT arrow keys to move the cursor of the terminal.

''' charming: event'''

import charming as app

@app.setup
def setup():
    app.full_screen()
    app.color_mode(app.RGB)
    width = app.get_width()
    height = app.get_height()

    # set the curosr at th middle of the screen
    # and hide it
    app.set_cursor(width / 2, height / 2)
    app.no_cursor()

points = []

@app.draw
def draw():
    app.background(' ')
    for i, p in enumerate(points):
        c = app.map(i, 0, len(points), 0, 255)
        app.stroke('@', (c, 0, 0), (c, c, 0))
        app.point(p.x, p.y)

@app.cursor_pressed
def cursor_pressed():
    # add a point if cursor moved
    x = app.get_cursor_x()
    y = app.get_cursor_y()
    points.append(app.CVector(x, y))

app.run()

image

Helpers

Because Charming use the terminal as the canvas to paint, so it is impossible for you to print some information to the console. Instead you can use the print function from Charming to print some information to file named charming.log.

import charming as app

n = 1
s = 'hello'
d = {
    'name': 'charming',
    'awsome': True
}
t = (1, 2)

app.print(n, s, d, key=t)
# charming.log

DEBUG:root:123, ['h', 2], {'name': 'charming', 'awsome': True}, {'key': (0, 1)}

Future Works

  • Tests and Bug Fix: There may be some bugs because Charming have just been simply tested, so one of future works is to test and fix bugs.
  • Improve Performance: Now both of the frontend and the backgend of Charming are implemented in Python, there's plan to rewrite backend in Rust or C++ and refactor some render algorithms such as polygon filling to achieve a better performance.
  • API Enhancement: Add more cool APIs accroding to the feature of terminal and character.
  • More Examples: Write more interesting examples.
    • ascii art animation
    • character-style generative art
    • terminal game
    • expressive data visulization
  • Better Documentation: Write usages, parameters, returns, examples for each API.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

charming-1.0.0a0.tar.gz (186.7 kB view hashes)

Uploaded Source

Built Distribution

charming-1.0.0a0-py2.py3-none-any.whl (45.9 kB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page