Emergent Lettering

Emergent Lettering

I saw a clip of video a few days ago of an interview where the people on stage had a huge wall-sized display behind them and on which there was a grid and of giant "pixels"/blocks. Gradually, letter by letter, a sentence was displayed on the screen in a pixelated font over a period of a few seconds, paused, then disappeared again, letter by letter.

The effect looked really cool and I thought it might be fun to replicate something like it in processing. All of the following code is split into chunks for understandability but in reality this was all in one sketch file.

I started with the background grid:


class Grid:
    
    def __init__(self, colour, spacing):
        self.colour = colour
        self.spacing = spacing
        
    def draw(self):
        stroke(self.colour)
    
        for x in range(0, width, self.spacing):
            line(x, 0, x, height)
        
        for y in range(0, height, self.spacing):
            line(0, y, width, y)

Followed by the idea of each individual letter than can be shown or hidden as required. By default each letter will be hidden:


class Letter:
    
    def __init__(self, letter, x, y):
        self.letter = letter
        self.x = x
        self.y = y
        self.visible = False

    def display(self):
        fill(0)
        textAlign(LEFT)
        
        if self.visible:
            text(self.letter, self.x, self.y)
            
    def makeVisible(self, v):
        self.visible = v
            

The most interesting bit comes when we put the letters into sentences. We need to split the words into individual letters and work out where on screen they should be shown.


class Sentence:
    
    def __init__(self, words, x, y):
        self.sentence = []
        self.show = True
        
        for character in words:
            letter = Letter(character, x, y)
            x += textWidth(letter.letter)
            self.sentence.append(letter)
    
    def draw(self):    
        fill(0)
            
        for character in self.sentence:
            character.display()
     
    def indices(self, visible):
        return [ i for i, x in enumerate(self.sentence) if x.visible == visible]
    
    def reveal(self):
        
        try:

            # find items still remaining to show or hide
            indicesToProcess = self.indices(not self.show)
            # pick one
            randomIndex = int(random(len(indicesToProcess)))
            # use this letter
            index = indicesToProcess[randomIndex]
              
            self.sentence[index].makeVisible(self.show)

            # check if we are done showing or hiding
            visible_states = [l.visible for l in self.sentence]
            
            if self.show:
                # switch if all are visible now
                all_shown = all(visible_states)
    
                if all_shown:
                    self.show = False
            else:
                # switch if none of the letters are visible
                any_shown = any(visible_states)
                if not any_shown:
                    self.show = True

        except exception as e:
            print(e.message)

The drawing code only shows letters in the sentence which are visible. Once all the letters have been shown, we flip into hiding the letters and when they are all hidden we flip back to showing again. Each time the reveal function is called it tries to find a new letter to show or hide. The indices function uses enumerate to work out the indices of items in the list that match either hidden or visible. The python all and any functions also come in handy when trying to work out when we need to flip the state of the showing or hiding in the main loop.

Finally, the main invoking code puts it all together here:


message = 'hello world'

background_colour = color(random(0, 255), random(0, 255), random(0, 255))
grid_spacing = 8
grid_colour = color(128, 128, 128)
        
def setup():
    global sentence, grid
    
    size(1000, 500)
    textSize(64)
    textAlign(CENTER)
    strokeJoin(ROUND)
    
    frameRate(5)
    
    grid = Grid(grid_colour, grid_spacing)
    sentence = Sentence(message, 10, height / 2)
        
def draw():
    background(background_colour)
    grid.draw()
    sentence.draw()
    sentence.reveal()