#!racket/gui
(require pict)(show-pict(let*([c cc-superimpose][f filled-ellipse][h hc-append][v vc-append][r rotate][p"pink"][E(c(f 85 140 #:color p)(f 50 90 #:color"maroon"))][I(c(f 60 60 #:color"white")(f 30 30 #:color"brown"))][M(dc(λ(D X Y)(define-syntax-rule(S a ...)(send D a ...))(let([P(S get-pen)][B(S get-brush)])(S set-brush(new brush%[style 'transparent]))(S set-pen(new pen%[width 5]))(S draw-arc(+ 1 X)Y 48 48(- pi)0)(S set-brush B)(S set-pen P)))50 50)])(v -40(h 20(r E .2)(r E -.2))(c(f 220 220 #:color p)(v 20(h 40 I I)(v -25(filled-rectangle 8 30 #:color"darkbrown")(h M M)))))))
Here's the TIO link. Note that TIO can not display the pict
, hence why it shows an error. Feel free to use the link as a means to verify byte count and/or copy code into a local file.
Screenshot from DrRacket

Yeah, I'm using Light Theme at 12am xD
Explanation of the code
This explanation uses the ungolfed version of the program, but I will explain what I did to golf it up along the way.
On line one, we set the language to racket/gui
. This us to create GUIs and use some pieces of Racket's canvas API (we use the brush%
and pen%
classes to make new styles for them). Right underneath the language statement, we import the pict
package. This package sort of works like JavaScript's Canvas API.
#lang racket/gui
(require pict)
(show-pict ...)
We then start the program by calling show-pict
which renders a picture to the screen.
While Racket has already made picts for fishes and files, it doesn't have a pict for an easter bunny. So let's make one! To make the eyes and the ears, we can use filled ellipses. In pict, they are defined simply as filled-ellipse
. The function receives the following input:
(filled-ellipse width height #:color [fill-color])
Now we want to center an ellipse inside of another (the outer one for the outer ear/eyeball and the inner ellipse for the inner ear/pupil). To that, we simply use the function cc-superimpose
(standing for "align center x, align center y, on top of another pict").
(let ([ear (cc-superimpose (filled-ellipse 85 140 #:color "pink")
(filled-ellipse 50 90 #:color "maroon"))] ;; Inner ear
[eye (cc-superimpose (filled-ellipse 60 60 #:color "white")
(filled-ellipse 30 30 #:color "brown"))]) ;; Eye pupil
...))
Now comes a trickier part: the mouth. I say it is tricky because it involves using the canvas context API to draw arcs. pict
provides a neat little function called dc
which allows use to access the d
rawing c
ontext for the canvas. It accepts three arguments: a function that draws the pict using the context, width and height. The function that it receives must also receive three arguments, but this time, they are: the drawing context and the current x
and y
positions for the pen and brush.
(dc (lambda (dc dx dy)
...) width height)
To tell the context to draw something or to configure the pen and brush, we use the send
function to call the one of the object's methods. In our case, we set the brush to 'transparent
.
(send dc set-brush (new brush% [style 'transparent]))
Now that the basic functionality is mostly explained, let's create the mouth once and for all.
(let ([ear (cc-superimpose (filled-ellipse 85 140 #:color "pink")
(filled-ellipse 50 90 #:color "maroon"))] ;; Inner ear
[eye (cc-superimpose (filled-ellipse 60 60 #:color "white")
(filled-ellipse 30 30 #:color "brown"))] ;; Eye pupil
[mouth (dc (λ (dc dx dy)
;; Save old pen/brush state
(define old-pen (send dc get-pen))
(define old-brush (send dc get-brush))
;; Set new styles for pen/brush
(send dc set-brush (new brush% [style 'transparent]))
(send dc set-pen (new pen% [width 5]))
;; Draw an arc of width/height 48 at (dx+1,dy) counter-clockwise
(send dc draw-arc (+ 1 dx) dy 48 48 (- pi) 0)
;; Reset to default pen/brush for `dc` function
(send dc set-brush old-brush)
(send dc set-pen old-pen)) 50 50)])
...))
One thing to note is (+ 1 dx)
. I had a rendering issue where the arc would be one pixel to the left of the nose, and this seemed to fix it. I think it might be caused from putting the arcs side-by-side using hc-append
.
Let's add these facial features to the actual face.
(let ([ear (cc-superimpose (filled-ellipse 85 140 #:color "pink")
(filled-ellipse 50 90 #:color "maroon"))] ;; Inner ear
[eye (cc-superimpose (filled-ellipse 60 60 #:color "white")
(filled-ellipse 30 30 #:color "brown"))] ;; Eye pupil
[mouth (dc (λ (dc dx dy)
;; Save old pen/brush state
(define old-pen (send dc get-pen))
(define old-brush (send dc get-brush))
;; Set new styles for pen/brush
(send dc set-brush (new brush% [style 'transparent]))
(send dc set-pen (new pen% [width 5]))
;; Draw an arc of width/height 48 at (dx+1,dy) counter-clockwise
(send dc draw-arc (+ 1 dx) dy 48 48 (- pi) 0)
;; Reset to default pen/brush for `dc` function
(send dc set-brush old-brush)
(send dc set-pen old-pen))
50 50)])
;; Ears + Face
(vc-append -40
;; Ears
(hc-append 20
(rotate ear .2)
(rotate ear -.2))
;; Face
(cc-superimpose (filled-ellipse 220 220 #:color "pink")
(vc-append 20
;; Eyes
(hc-append 40 eye eye)
(vc-append -25
;; Nose
(filled-rectangle 8 30 #:color "darkbrown")
;; Mouth
(hc-append mouth mouth))))))
The vc-append
appends picts vertically, and hc-append
appends picts horizontally. Both functions receive an optional spacing parameter as it's first argument. So the arcs for the mouth have no space between them, but the eyes have 40 pixels between them. To fit the nose and the mouth snuggly together, I used -25 as the spacing.
Putting the program together is as simple as placing the let
block inside of show-pict
.
A cleaner more declarative way
#lang racket/gui
(require pict)
(define ear (cc-superimpose (filled-ellipse 85 140 #:color "pink")
(filled-ellipse 50 90 #:color "maroon")))
(define ears (hc-append 20 (rotate ear .2) (rotate ear -.2)))
(define eye (cc-superimpose (filled-ellipse 60 60 #:color "white")
(filled-ellipse 30 30 #:color "brown")))
(define eyes (hc-append 40 eye eye))
(define nose (filled-rectangle 8 30 #:color "darkbrown"))
(define mouth (dc (λ(dc dx dy)
(define old-pen (send dc get-pen))
(define old-brush (send dc get-brush))
(send dc set-brush (new brush% [style 'transparent]))
(send dc set-pen (new pen% [width 5]))
(send dc draw-arc (+ 1 dx) dy 48 48 (- pi) 0)
(send dc set-brush old-brush)
(send dc set-pen old-pen))
50 50))
(define nose+mouth (vc-append -25 nose (hc-append mouth mouth)))
(define eyes+nose+mouth (vc-append 20 eyes nose+mouth))
(define face (cc-superimpose (filled-ellipse 220 220 #:color "pink")
eyes+nose+mouth))
(define easter-bunny (vc-append -40 ears face))
(show-pict easter-bunny)
How I golfed the code
While the let
statement is cool, its brother let*
is cooler. A normal let
statement doesn't allow other variables inside of its binding block to see each other. but this isn't the case with let*
:
;; Normal let - Can't use `f` in let binding
(let ([f filled-ellipse]
[red-circle (filled-ellipse 50 50 #:color "red")])
(cc-superimpose (f 50 50 #:color "yellow") red-circle))
;; Cooler let*
(let ([f filled-ellipse]
[red-circle (f 50 50 #:color "red")])
(cc-superimpose (f 50 50 #:color "yellow") red-circle))
Because I use cc-superimpose
, vc-append
, hc-append
, etc. a lot, I used let*
so that I could bind them to shorter names:
(let* ([c cc-superimpose]
[f filled-ellipse]
[h hc-append]
[v vc-append]
[r rotate]
[p "pink"])
...)
I then proceeded to rename all variables to shorter names, ear becomes E
, eye becomes I
, etc. After that was done, I started to ponder what I could do with the dreaded mouth send
s. I ended up creating a macro :)
(let* (...
[M (dc (λ (D X Y)
;; This macro will replace (S get-pen) with (send D get-pen)
(define-syntax-rule (S a ...)
(send D a ...))
(let ([P (S get-pen)] [B (S get-brush)])
(S set-brush (new brush% [style 'transparent]))
(S set-pen (new pen% [width 5]))
(S draw-arc (+ 1 X) Y 48 48 (- pi) 0)
(S set-brush B)
(S set-pen P)))
50 50)])
...)
Once that was done, I deleted all unnecessary spaces.
The golfed code and the code I used in the explanation are basically identical. If you don't want to open a new window, simply remove the call to show-pict
. Pict only displays well in DrRacket. If you try to print a pict to a console like CMD or terminal, you'd see #<pict>
which is Racket's way of telling you "Yes the statement returned something, but I can only tell you what type of object it returned.
Conclusion
It's 2am now as I write this conclusion, hope everyone enjoyed this little tutorial-ish explanation!
Have an amazing week!