Nicolas Seriot: Retro Coding Fun: Interactive Games on Your Printer with PostScript
About this talk
In this talk, Nicolas Seriot explores the programming language PostScript, emphasizing its capabilities and potential uses beyond printing. He describes PostScript as a full-fledged programming language with origins at Xerox PARC, later popularized by Adobe. The speaker illustrates how PostScript can be used to create interactive applications, such as a tic-tac-toe game and chess, showcasing its functionality with practical demonstrations. He also discusses the limitations of PostScript and contrasts it with PDF as a document format. Ultimately, Seriot encourages attendees to see traditional printers as powerful tools for creative expression and programming.
Full transcript
Hi there. Can you hear me? So, please come and sit down. Welcome to this session. My name is Nicolas Seriot. I am a software engineer, probably like you, and I'm also a manager in a Swiss bank. I have also a degree in management. I live in Switzerland, in the mountains. I love nature and I also am to ultra ultra trail and and running in the I really
enjoyed Vilnius. We had a short seeing run with my friend Pierrick here yesterday. We we lived the Lithuanian way of life. We tasted class and ate pork ears, very exotic for us. But most importantly, we are here to enjoy and learn from this conference. And I would like today to build up to build on top of the closing session from yesterday where GG explained how we came
into this profession, into this industry, because we liked building cool stuff. For sure, we know we are not grown-ups and we have to live our lives. We are into a corporate politics and product management and all that kind of stuff. But today, I would like to focus on why we are in this industry in the beginning. So, what is cool stuff? For me, and I assume for
you as well, and if you are in this room now, it is probably the case. Cool stuff is deep thinking. It's about solving puzzles, brain teasers, exploring the technology from a theoretical standpoint, like mathematical thinking, but also from a practical standpoint by trials and errors. It's about creating new things, about building stuff, about hacking, and sometimes it is almost close to art. What is cool things? Let's
take this example. Wait, sorry. This is cool thing. I wanted just to explain before I launch the movie. So, we are reading a PostScript file and piping it to Netcat, sending it towards a printer. Right. So, this is cool stuff. And I would like to take you with me in my today with the very same food and drink that I intake when I do cool stuff, almonds,
Swiss dark chocolate, and hot coffee. So, cool stuff today, because I like cool stuff is very close to weird stuff, right? And I like weird stuff. And today we'll focus on PostScript. So, it's very likely that your single experience with PostScript up to now is printer errors. This is typical garbage that you can see sometimes when a document cannot print properly. But today, we we'll take a
shift from it doesn't work and ask ourselves those three questions. Can I program in PostScript? If so, can I play against a printer, a machine, physical hardware? can I do something else with PostScript on the desktop? And to these three questions, we will answer with a triple yes. So, what is PostScript as a language? It is a full-fledged programming language. It comes from the Xerox PARC, so
so many cool stuff were invented there, as you know. It's evolved and was published and released and licensed by Adobe, who very smartly produced software that produced PostScript, such as PageMaker, Adobe Illustrator, but also they licensed the PostScript interpreters for hardware manufacturers and resellers, such as Apple. after that, so PostScript is still a thing, it's still alive, still used, but it evolves as well. The evolution tree
evolves into display server with the NeXTSTEP operating system. But also, PostScript laid the ground for the PDF format. More that later on. What does it look like? This is typical PostScript code. Well, it is highlighted for better comprehension, but this is typical PostScript code. It is very compact, as you can see, only a few hundreds of bytes, whereas a similar PNG would have taken much more space.
So, it's very interesting as a format when you want to send data from a computer to a vector format, meaning that, of course, you can zoom as much as you want, very interesting at the time where laser laser printing became a thing. How do you write PostScript? Well, it's very inconvenient to write PostScript. Either you use Notepad or such. There is no IDE, there is no Visual
Studio or no Xcode for PostScript. So, what what I did is I took that text editor for macOS, Subethaedit, and I wrote a PostScript mode for it. But you can write PostScript without such a mode, but it's more convenient. You cannot really debug PostScript. Merely, you can what you can do is put traces in the code, such as pstack to print the stack and stop, and you
see where you stand, how the stack is. How do you run PostScript? So, either you run it on an actual printer or you use Ghostscript. This is the main PostScript free and open source interpreter. I will show you right now. So, there is there are a couple of demos in this session. So, ps2pdf, which is part of the Ghostscript package, let's say. stairs.ps and open stairs.pdf. This
is the document um with those code before. But you can also write directly PostScript into an interpreter, into live interpreter. So, here I will launch Ghostscript. And I can start typing code. So, look. Right, green, blue. I pick the blue color. Um x y width height, I draw a rectangle. Same for the next rectangle. I pick a color, I pick a a font, and I can I
can type whatever I want. Okay? So, you can have an interactive session with PostScript. What about the language itself? It's a super simple language. It may sound intimidating at first, but believe me, it's much more simple than any language that you know. You have basically two stacks, and the main one is a stack for operands. You push one, you push two, you push an operator, so it's
executable. It will, of course, leave three on the stack. And beside that, you have a stack with dictionaries where you basically store the variables and the procedures. Let me show you what command summary looks like. So, super super basic. You have the count operator, it takes something and count and the return yeah, something on the on the stack. So, it's really a very simple language, very easy
and and nice to begin with. I put some other pointers that you can have a look at later on if you're interested. But instead of having an academic lecture on the language, I chose to show you some code examples to for us to to feel to start feeling how PostScript feels, right? So, take this one. To to draw this nice this nice circles, first we pick the
blue color. So, we understand that there is an implicit graphic context. You do not have to import anything. There is no, by the way, standard library or anything like that. You can define variables, and all variables are put in the dictionaries, remember. So, for the N key, we assign the 10 value. We have control structures, here a for loop. And we have as graphic context is implicit,
we can have those nice primitive arc stroke. So, it's very very concise. It's something that you will keep on seeing during the whole presentation. Shutter, it's one of the first example of, let's say, computer art, computer-generated art. At time, it was not even a thing. We can have So, what you see is here all those little squares. As we go downwards, they get translated and rotated more
and more. But what is interesting here is what sum of the keywords we see on the right. We see that we have a random operator. So, it means that if you print this code twice, you will not get the same document. Okay? We can see that it is very super easy to apply geometric transformations, and we could even try to minimize further down this code single free
lines. Yet another example. This is an image at some point I stumbled upon on it on the internet, and I started wondering, how could I reproduce it in And this is the kind of mental challenge that I I love I love doing while commuting, you know? And here again, we can see that we assign to the P keyword a block of code that is a procedure. It
is basically how you define a procedure, And with this single code, you can draw this this image. And believe me, I tried a much longer code before I could show you this very concise code. One more example. Amstrad CPC color palette. This is a color palette so uh used it in the old days. What if you wanted to draw those two cubes? Uh well, this code does
it. And so, maybe I can just No. Okay. Uh believe me, this code does it. Uh what is important here is like we have a GSave, GRestore. So, we save the graphic context, we restore it. We have geometric transformation, but most more interestingly, we haven't seen that yet, maybe, is that we can pass blocks of codes around. So, it is like anonymous functions, and it it gets
it makes it super easy to code in PostScript and super nice. It feels very pure, you know, when you start thinking what PostScript idiom I could use to achieve my task, and it's a it's really nice to program with. Um maybe one more exa- example. Uh this is So, PostScript per se doesn't have access to date and time. But, the environment around the PostScript interpreter may have
access to such dictionary. And most of most of time it is the case on a printer in Ghostscript. So, here I get calendar dictionary current def params. We read the hour and the minutes, and each and every time we will print this code, we will get this a nice clock picture. How does it work? It's Again, it's super super easy. So, we draw this You can see
my pointer, right? We we draw we draw the the circle here. We draw the side marks each and every 6°. You can see the if-else operator construct. It takes a boolean parameter, a first block of code, second block of code. If the boolean is true, we execute the first one. So, here it is a a small thick Uh no, it is a big the big thick. And
or we we execute we draw a small Then, we have to draw the the hands. Minute hands is 6° times number of minutes um to get the right angle. And for the hour, same logic, plus we take into account contribution, let's say. So, you start to understand that PostScript is really a full-fledged language. It is a Turing complete language, and as a proof, I implemented this Brainfuck
interpreter in You may know that that language, despite its weird name. It is super interesting because with with only these eight operators, you can have the pretty much same calculation model as C++, Java, or any modern And if you can implement a Brainfuck interpreter in any language, then that language is also Turing complete because Brainfuck has been proven to be Turing complete. So, you you may think,
"Okay, well, okay, it's a it's a full-fledged language. Okay, so what?" Why is it interesting? I didn't know. Okay, now I know. So, it's not perfect. It has specific capabilities, and it lacks also many capabilities that you would expect from a modern programming language. No system calls, no network access, no threads, no Unicode support, no strong typing. It's super limiting. Yet, it is super interesting because it
runs many laser printers. It has access to file system. PostScript is very much inspired by the Unix philosophy, where everything is a file. So, we can provide data through standard input, retrieve data through standard output and standard error, and interact somehow with the file system around the PostScript interpreter. We'll see that in a few minutes. How does it relate to PDF? That's that's an important uh uh
or programming language question PDF is like a stripped-down version of PostScript somehow. PostScript as a language as a Turing um it can do anything. So, when you start a PostScript program, you never know if it it will stop. You never know the number of pages, let's say, that your document will have. And this is not um properties that you expect from document format that you want to
where you want to be able to print, let's say, the 10th page out of 20. You you want to send it around the internet and to share the final document where the the form appearance will not change and will be stable. So, sometimes being a powerful language is not an advantage. Sometimes we don't want a programming language, we want a document format such as PDF. So, you
can think of it as of PostScript documents as programs, whereas PDF documents are really more akin to a document format such as PNG, okay? But, SVG, maybe. Okay. So, I tried It was quick, but I tried to show you what is PostScript, where it does come from, what it looks like, and what it can do and cannot do. But, we still haven't created much Now, back to
the question. Can we play against a printer, which was my real motivation behind this research? For that, I bought a laser printer. I didn't have one. Uh that one is pretty cheap, I think, for what it is. And um it it features a PostScript interpreter with a network access. With Wi-Fi, from your sofa, you can program or access your your printer with a lot of coffee and
a lot of lot of chocolate. So, the very first attempt was to code simple tic-tac-toe game. This is again tic-tac-toe. So, connected on the printer with netcat. Super smart kid. You want to get the printer. So, this is feasible. And this is a simple challenge, let's say. And for each of the challenges of the games that I will show go go through today, I will highlight the
main challenges, The main challenge here was not the program itself. It was really the communication with a machine. So, in the very first line here, you can see how Maybe I can show you this way. So, you can see how you can send a file to the printer. But, it is one way. Something to know is that you can connect directly on the printer with netcat, the
IP address plus the port for PostScript interpreter. You type PS executive, and you get into an interactive mode where you can type whatever you want. It gets executed, showpage, it prints the page. But, how do you get a a bidirectional communication? It kept me busy for several hours. And it turns out that it is simple simply using the capabilities of bash and cat and Unix capabilities, let's
say. Um where we cat the file, plus we keep the communication open while piping it to netcat. And this way, we we have a bidirectional communication channel with a printer. Now, from the printer perspective, it is simply a file. Data comes through a file that you simply read line after line. Now, in terms of data structure and algorithm, it's a super simple. The data structure is an
array of nine bytes um filled with either dot or X or zero. Maybe you can try to understand this procedure. Number of free position, we put zero on top of the stack. We iterate through the array, and whenever we meet a 46, that is ASCII code for dot, we add one to the value on the top of the stack. And that it. This is it is akin
to the reduce function, and it's a very simple way and effective to count number of free positions. Uh the algorithms um I I cannot say AI yet, but from the printer perspective, it reads the it reads the human input. If someone has won, exit. Uh else, if the printer can win with a one move, it does. Else, it will try to block human. Else, it play randomly.
And it turns out that this algorithm is super effective, or at least entertaining, while playing with these little silly games. Now, more interesting, or more complicated. A chess game. I wrote the chess game, as you saw in the very first video, for the printer. So, I will take you through the various challenges that I encountered. But, before I will uh still um show you a little demo.
Uh PS chess. So, now it is being executed in Ghostscript, right? But, it the very same thing on the on the printer. So, from the player perspective, this is what you see, and you can I will play, for instance, uh C2 C3. And then now G1 F3. So, you you get the idea. So, when you start writing such an or more than a few hundreds of lines
of code, you have to structure your your your program with dedicated files. We try to separate the logic from drawing, and it's quite easy to do in PostScript because you can include another file with why is the parenthesis parenthesis are the string marker in PostScript? It's just a file name plus the run operator, so include the file. So, you can start building like a tree of includes.
Um in terms of data structure, yeah, you can see here on the top left a part of the screen here. This is a data structure. This is a simply 64 strings 64-byte strings where where we represent the the chess pieces. And I also had to build kind of unit test framework. It's a It's a big name, but it's a quite easy and simple as the Uh first
first code block and second code block and we assert that the two are equal or or we crash and we print the name of the test. So, I had just to let you know that to undertake such a challenge, you have to put some things some structure in place and that is how I I did tackle it. I also had to come up with naming conventions. PostScript
is quite a permissive language when it comes to to naming. It is not typed or it is typed, but there is no type check in in in any way. So, this this is how I write I name procedure that that is that will tell if a player can play given a specific board. Can play player board and it will return a boolean. So, for me, it's clear,
but it is not maybe for everyone. This is my personal conventions. And it is really akin to this more familiar construct can play with take two arguments and we leave the result on the stack a boolean result. And it was really a necessary to keep sanity while writing such a program. UI design really nothing complicated is made of move to line to, so like you know 10
lines and you have all your all your pieces. In terms of now AI, let's say we have to make the machine think or act as if does think. And for that, I chose to implement a quite a simple algorithm that comes from Michniewski. It's apparently a thing in a chess programming. So, the idea is that each piece has a specific value. We see the pawn, it does
weight 100. And the queen 900. And depending that weight is weighted given the position of the piece on the chessboard. So, you see it with with a knight and uh and you get the idea. So, at the end we sum everything up and we get this score A sum of all the scores and the black will try to decrease this score and the white to increase this
score to optimize that value. So, given that, you can use simple algorithms such as minimax. So, basically, we try to see So, here the white have to play after the black and they will try to minimize their opponent's advantage. So, they will choose the move after which the opponent will not get much points. So, it can be as complex as you want like this. You could come
up with You can say, "Yeah, at the end of the game, I change the bits the the table and so on." But that was not the the point in this in this project. The The point was to make the printer play with a simple algorithm and this is what was achieved. Now that we've made the printer actually play games against player through netcat, what else can we
do with PostScript? Can we forget the printer and do something nice or interesting under the stop? Yes. I will show you. Sokoban, you probably know this game as well back from the '80s. Wait. So, I I need two terminals here and you will understand why. So, Soko Sokoban, you have that little guy that moves around. He has to put the boxes on the goals. This is level
zero, so super easy just to check that everything is working. And you have a more complicated levels. And you can go backwards as well. So, how is it done? A couple of challenges of challenges here. First, the images. They are described in the code, so I can also maybe it's the source code. It's not big, huh? Look, including the images, we have less that than 700 lines
of code, all right? So, they feature images embedded in the code red green blue like DD5 on the free on three bytes. Um note that the the images in is inverted or they have the Y coordinates inverted because in PostScript the Y goes upwards and in in the image it goes downwards. The levels they are represented with strings, so you can super easily share or or or
write your own levels. It is a standard way. I did not come up with that. It is already a standard thing apparently and you can represent each and every level with only those seven bytes as a man, the man on goals, the box and so on. so it was not complicated to achieve. What was complicated again was handling the user input because PostScript is not intended to
be an interactive language. So, the idea I I came up with is to create a pipe and to cut the code so sokoban.ps with the pipe and have the terminal write the characters in the file. But without needing enter because while playing chess, I had to type my move, then hit the enter uh keys on the keyboard. And I don't want to do that with Sokoban. So,
we have to enter this raw mode and put send the characters to the pipe and on PostScript end, it is interpreted as reading in a file. Undo manager same I started building something quite complicated trying to replicate an object-oriented approach with with a structures and methods attached. It turned out that it was much more simple forget what we know and try to think with the language idioms
and only with these 10 or 12 lines of code, you can an undo manager implemented as a ring buffer and the idea is basically that before changing something on the on the on the on the world, you store the former state. So, if you want to go back, you just restore the last last example, Tetris. You all know and love Tetris. Oops. Let me show you the
code. Again, it's a super simple. Um yeah, not even 600 lines and it is self-contained. No imports, no include, no image, nothing. Just that's just this code. So, how does it work? I will demo. Yeah, you would have believed me, but so see it working. So, it's a real Tetris with uh various levels and uh speed increasing as we go through the levels. You can clear multiple
lines in a row and have a the the the and the score will increase accordingly. So, how how is it done and what challenges had we to to overcome in this project? In in Sokoban, I could enter making stroke immediately without hitting enter, but it's not exactly what I want to do here. Sokoban was kind of blocked expecting keystroke before going on. Here, we want Tetris to
to to continue dropping the pieces even if you don't enter anything. So, the trick here here is to have GhostScript reading the file continuously till it takes txt and it reads all the time and if nothing was found, it goes on and if something was found, it interprets it and clears the In terms of memory storage, well, we have these seven tetromino as they call it them.
Uh they have names T J Z O and they can have rotated states. So, let's take the T, it can be rotated upwards, rightwards, downwards, leftwards. And this can be encoded in a 5.5 Boolean matrix, hence what you see on the top right of the screen. These Tetrominos are so they are stored here. See? With their possible rotations. And it means that to store the current one
plus the next upcoming one, you only need 4 bytes. Because the first one is stored with the its code, so it's a T. So, it's code zero and its rotation state. The rotation is So, it starts here. All right, okay. Yeah, depending on how you you set up things, it could be a two. But you understand the idea. You store the number of the Tetromino and its
rotated And same for the upcoming Tetromino. So, nothing really scary, just these matrix, this array, and these codes. The Tetromino this pole here in the coordinate five zero, if we assume that we start at minus two, it is how Tetris is working. And so, it's a again an array, so a string, where we store nulls. But um to to store specific Tetromino, we store them with their
code. So, one is a the J, six is a I, and so on and so forth. And to distinguish the blocks that are dropping, I just add 10 because we know that from zero to six, it is a bricked Tetrominos, but if you add 10, they are active. And whenever they are, they cannot move further down, we remove 10 and and we are done. So, with very
simple code, with very simple algorithm data structure, we can build a fully functioning game quite easily. Another interesting feature of Tetris is the the Tetromino spawn. How how Who decides what piece you will get next? The idea here in in many Tetris implementations is to have what we call a random bag. All the seven pieces are like shuffled in a bag and you take one out. And
how can you implement that in PostScript? Again, this is very easy. This is an array of Boolean of length seven. And so, you take a random one, you mark it as true. So, if I take the O, so index three, here I will um I will put true in that in that square. And again and again, and when everything is marked as true, I start again. This
ensures a kind of fairness across across the game. Same, level, speed, and scoring. You may you may think as first approach, well, it's complicated, it's will get messy, and all. It turns out that it is super simple because you count the number of lines that are cleared. The the level is just this number of cleared lines divided by 10. And you take that that level value to
decrease the delay after which pieces drop. Each tick gets shorter and shorter. And for the scoring, same, there is a kind of very simple formula, just one line with which you can implement the whole scoring system. So, it's very nice. It's like when you play flipper, you know, you want to or pinball, you you want to compare how you played with how others play. That's part of
the game, so you have to store high score file. And I was like, I it's not feasible in PostScript. And you know, the idea keeps on going, really, is it possible or not? So, I started when I read the PostScript documentation for sure. And it turned out that that you can store arbitrary content in into arbitrary files on the file system. It may be that some PostScript
PostScript interpreters do not allow it or some file system are locked, but it's what's not my experience. So, both on on GhostScript on the on the desktop and on the printer as well, you can store whatever you want. And those data are persistent across reboots. And this is Look, I put the the puzzle a piece of for Cyberwars conference because it's a link with that security aspect.
So, I tried it at work. I created Oh, there is a mistake. I created a file on the on the printer file system, and you can do so just by printing a PS file. So, you print a file, but it also it will it may also write data on the file system. And another document or anyone connecting to the printer will be able to retrieve that information.
Exactly as today opening Keynote. You're not supposed to do that, but technologies works like this. It allows that. It's possible, but no one would use it except maybe hackers in the broader sense of the term. You could do many much more things in PostScript. You could do real animations with this basic technique. So, here we have a loop. We clear the screen, we draw our stuff, we
sleep an arbitrary amount of time, and we start again. And this is how we can nice-looking simple animations. We could do 3D. If I go in into GhostScript, I paste this code, I have a nice 3D-like uh scene. We could have music. I agree that it would be nice to have music while playing Tetris. We could if I was if I were running Linux because on macOS,
I didn't find a MIDI synthesizer to which I could send the raw MIDI messages. But if you can find such a MIDI synthesizer, then you will hear the music. For instance, by sending those MIDI messages to STD error, standard error. So, on the paper now, you have everything to write more or less simple games such as Pac-Man or Out Run. You have all the basic bricks and
all the concepts in place and accessible to write such games. we went for these three questions. We asked ourselves, can we program in PostScript? Yes. Can we play games on printer? Yes. Can we write games for desktop in PostScript? Yes. Takeaways. You won't overlook printers anymore. They maybe the most hated pieces of appliance, you shift a bit your look at them, and all of a sudden they
start becoming cool. Cool stuff as GG mentioned. Cool stuff that are very constrained. The language is ancient, forgotten. I don't have a printer. I don't know how to connect to it. Those are constraints, and they really foster creativity. And one I think it was one of the first Macintosh engineer who said that engineering is really working within constraints. And I believe that very much. You add constraints,
and all of a sudden it gets a challenge. Can I Can I Can I do something within that constraint? Can I build new things? Use unexpected tools to achieve unexpected results. And at the end, it's about hacking, but hacking is not just about security. It's about also about fun and for me a kind of art. Kind of art in the sense that you take something and you
you use it for something else that it was intended to be used. Um if you are interested in the in the this topic, you can go on my website, seriot.ch. There are plenty of PostScript resources and further explanations. I have 2 minutes left, so I have a more cool stuff, two slides. This one is a a poly so-called polyglot files. It's a file that is both Wait,
I have just time for demo. It's a file that is if I open it, it's a it's a PDF file, right? But if if I give it to GhostScript, it is a PostScript file. Why? Because it is both a PostScript and a PDF file. I have no time to explain that, but you will see. Basically, the PostScript part interpreter will stop here, and the PDF interpreter, let's
say, will start at the bottom and go through the reference index and so on. And last cool stuff is it turns out that there was an obfuscated PostScript context. It's true. It's based on the C obfuscated context in back in 1993. And that Japanese guy wrote So, here it's already a bit expanded, but a piece of software that a full-fledged ray tracer. Last very last demo um
Yeah, so this very tiny code actually draws a 3D scene. And I wrote a full blog post probably here. Yes. About how by dissecting and analyzing how it works, I managed eventually to reduce it further down from 760 bytes down to 727 bytes. So, that's it for me. Um I hope you enjoyed the talk. And maybe if you only take away one from this talk is really
I think we are builders, creators and we are people who like to explore technology and being challenged by constraints. That's how I define myself and I think probably maybe you too. So thank you so much and I'm here for questions. Of. Next challenge no, not Doom. It would take too much time but I already somehow proved or demonstrated that all the bricks are in place so there
is not much challenge in terms of discovery discovery or innovation or building new things because we know we could do it. So I don't really see the challenge and interestingly Doom was implemented in the PDF couple of months ago by someone else but it was really not the same approach on the PDF side it was more by leveraging the JavaScript capabilities embedded in some PDF uh engines.
Uh no, I don't think that all printers support PostScript fully. Um some have PostScript interpreters embedded for performance or quality purpose. I think that low-end laser printers may support PostScript. I know there is something called PCL. I think I believe it is something akin to PostScript but I'm not sure if we can do the very same thing on PCL interpreters. Thank you so much.
More from this event
See all 73 talks →
Tomas Lekavicius: Building Tech Product Offer
42:08
Alisa Dammer: Science and Tech Backed Approach to Increase Productivity
44:53
Roy Wasse: The Definitive Answer to Measuring Developer Productivity
44:47
Pierluigi Meloni: You’re a Great Coder? That Alone Won’t Get You Far
44:47