A heart.lisp I wrote with my 6 year old daughter
That's a great start! Looks like a lot fun.

I would do only a few things slightly different. Some of them a real improvements and some are style preferences of an old Lisper... ;-)

First function: PRINT-LINE

(defun print-line (&amp;rest args)
  (loop
     :for type :in args :by #'cddr
     :for num :in (cdr args) :by #'cddr
     :do (loop :repeat num
            :do (princ (ecase type
                         (:s " ")
                         (:h "❤️")))))
  (format t "~%"))


There are two real improvements:


don't use a &amp;rest arg, pass the list directly
the outer LOOP can be written slightly simpler with destructuring


New version:

(defun print-line (line)
  (loop for (type num) on line by #'cddr
        do (loop repeat num 
                 do (write-char (ecase type
                                  (:s #\space)
                                  (:h #\❤️)))))
  (terpri))


The second function: PRINT-LINES

(defun print-lines (data)
  ;; ensure there is a new line
  (format t "~&amp;")

  (dolist (line data)
    (apply #'print-line line)))


There is one real improvement:


don't use apply, just pass the list directly


New version:

(defun print-lines (data)
  (terpri)
  (dolist (line data)
    (print-line line)))


The data variable

(defvar *data*
  '((:s 8 :h 8 :s 8 :h 8)
    ...
    (:s 19 :h 2)))


It helps to add a documentation string, or to give it a useful name.

(defvar *heart*
  '((:s 8 :h 8 :s 8 :h 8)
    ...
    (:s 19 :h 2)))


or

(defvar *data*
  '((:s 8 :h 8 :s 8 :h 8)
    ...
    (:s 19 :h 2))
  "The description of a heart.")