INPUT /  OUTPUT /  LANGUAGE





Images and Media

Text and boxes will only take you so far. This chapter shows how to display pictures, and how to play sound and video. All of them use the raw data you met in the chapter on values: your program reads a file, and sends its bytes straight to the console.


Sending a file

Here's the helper function from the chapter on values again, which sends a file as raw data. You'll use it in every example in this chapter:

import sys

def print_raw(filename):
    with open(filename, 'rb') as f:
        data = f.read()
    print('[' + str(len(data)) + '{', end='', flush=True)
    sys.stdout.buffer.write(data)
    sys.stdout.buffer.flush()
    print('}]', end='', flush=True)

The flushes make sure the text and the raw bytes reach the console in the right order, since Python sends them by slightly different routes.

Pictures: the img tag

An img tag displays a picture. Its primary field holds the picture file's data. PNG, JPEG, GIF (including animated GIFs) and SVG pictures all work.

print('<img ', end='')
print_raw('photo.png')
print('>')

A picture appears at its own size unless you give it a width and height, in which case it's stretched or shrunk to fit:

print('<img width=240 height=160 ', end='')
print_raw('photo.png')
print('>')

Changing the picture

Give an img a name, and you can replace its picture whenever you like, just like replacing the text in a span:

print('<picture ', end='')
print_raw('another.png')
print('>')

Sending a picture in pieces: .commit

Sometimes a file arrives in pieces, for example while it's still being downloaded. You can push each piece into the img as it arrives, using .push. But the picture can't be displayed until all of it is there, and the console has no way to know when that is. So when the last piece has been pushed, tell it with .commit:

<picture.clear>
<picture.push [first piece]>
<picture.push [second piece]>
<picture.commit>          <! now the picture appears !>

The console's icon

The console's icon field sets the small picture that represents your program, for example in a task bar. It takes a picture just like an img does:

print('<console.icon ', end='')
print_raw('icon.png')
print('>')

Example: a picture viewer

This program shows the pictures in a folder one at a time, with buttons to move between them. The console only has one img; the program replaces its picture each time the user clicks.

import os, sys

folder = 'pictures'
pictures = sorted(f for f in os.listdir(folder) if f.endswith(('.png', '.jpg', '.gif', '.svg')))
current = 0

def print_raw(filename):
    with open(filename, 'rb') as f:
        data = f.read()
    print('[' + str(len(data)) + '{', end='', flush=True)
    sys.stdout.buffer.write(data)
    sys.stdout.buffer.flush()
    print('}]', end='', flush=True)

def show(n):
    print('<picture ', end='')
    print_raw(os.path.join(folder, pictures[n]))
    print('>')
    print('<caption {' + pictures[n] + ' (' + str(n + 1) + ' of ' + str(len(pictures)) + ')}>')

print('''
<console.title {Picture Viewer}>
<box width=400 height=300 border=1 align={center},{middle} {<picture:img {}>}></n>
<caption:span {}></n>
<button {Previous} onClick=<putLn {/prev}>>
<button {Next} onClick=<putLn {/next}>>
<button {Quit} onClick=<putLn {/quit}>>
''')
show(current)

while True:
    command = input()
    if command == '/quit':
        break
    elif command == '/next':
        current = (current + 1) % len(pictures)
    elif command == '/prev':
        current = (current - 1) % len(pictures)
    show(current)
★
star.svg (3 of 3)
Previous Next Quit

The % in Python wraps the picture number round, so clicking Next on the last picture goes back to the first. (The file names are shown in the caption as text, which is fine for ordinary names. If a name could contain { or <, send it as raw data instead, as in the chapter on instance names.)

Sound and video

The audio and video tags play sound and video files. As with pictures, the primary field holds the file's data. Because there are several media formats, tell the console which one you're sending with the mimeType field:

FormatmimeType
MP3 sound{audio/mpeg}
WAV sound{audio/wav}
OGG sound{audio/ogg}
MP4 video{video/mp4}
WebM video{video/webm}
print('<ding:audio visible=false mimeType={audio/wav} playing=false ', end='')
print_raw('ding.wav')
print('>')

Nothing plays until you set playing to true. So a good pattern is to load your sounds once, at the start of your program, and then play them whenever they're needed:

print('<ding.playing true>')

An audio tag shows a small player on screen. For sound effects you usually don't want that, so the example above sets visible=false.

Controlling playback

FieldWhat it does
playingtrue to play, false to pause
positionthe current point in the media, in seconds; set it to jump somewhere else (once playback has started)
looptrue to start again from the beginning each time it finishes
durationhow long the media is, in seconds (only once it's ready; see below)
controlstrue to show play, pause and volume controls on a video

Media events

FieldHappens when...
onReadythe media has loaded enough to play, and its duration is known
onPlayplayback starts
onPauseplayback is paused
onEndedplayback reaches the end
onErrorthe media can't be played

For example, this tells the program when a video has finished, so it can move on to whatever comes next:

print('<movie:video width=320 height=240 controls=true mimeType={video/mp4} playing=false '
      'onEnded=<putLn {/movie-ended}> ', end='')
print_raw('intro.mp4')
print('>')
print('<movie.playing true>')
Media files can be large, and everything you send has to fit in the console's memory. Keep sound effects short, and videos small. If you need something long, split it into short pieces, and load the next piece while the current one plays.

Try it

  1. Add a slideshow button to the picture viewer that moves to the next picture every three seconds. (Hint: a timer can send /next.)
  2. Make each button in the noughts and crosses game play a short sound when it's clicked.
  3. Show a picture that gets bigger each time it's clicked.
  4. Play a piece of music, with Play and Pause buttons that work entirely inside the console.