Categories: learning

RSS - Atom - Subscribe via email

Visualizing the balance of speaking in my French conversations with tutors

| french, python, learning, visualization

I've been studying French since November 2025 because I want to help my daughter learn it. After about eight months of taking pronunciation tutoring sessions once or twice a week and writing my journal in French, I decided to try conversation-focused tutoring sessions in June 2026. These sessions help me get used to talking and identify the gaps in my active vocabulary.

These tutoring conversations feel quite different from other conversations I have. In real-life conversations with my husband and daughter, we usually leave a lot of space between sentences. When I'm hanging out with other people, I prefer to listen more than I talk. Tutoring sessions are different. I am investing time and money in practising talking. I can get a lot of listening practice from YouTube, so it's less about listening to people, although I still want to practice asking follow-up questions and connecting what the tutor says with my personal experiences.

In those conversation classes with French tutors, I want to get a sense of how much I'm actually talking. I was motivated to visualize this after some sessions with a tutor who tended to go into lecture mode. I found myself needing to be a bit firmer about directing the conversation instead of slipping into a more passive role.

I use OBS to record my sessions (with the tutor's permission, of course). I've configured it so that my microphone goes on one track and my computer audio goes on another track, which makes it easy to analyze my side of the conversation without needing to use speaker diarization.

I used Google Gemini to generate a script that uses Silero voice activity detection to analyze my side of the multi-track MKV recording.

Here's the file it generated. file:///home/sacha/proj/speech/vad-file.py

I call it with:

for FILE in ~/recordings/*conversation*.mkv; do if [ ! -f "$FILE-vad.txt" ]; then python vad-file.py -f "$FILE" -t 2 sacha 3 tutor -s 500 -o "$FILE-vad.txt"; fi; done

Then I can make a table analyzing different recordings:

(sacha-vad-tutor-make-table (directory-files sacha-recordings-dir t "-vad\\.txt\\'"))

Definition: sacha-vad-tutor-make-table

The green bars represent the time that I spent speaking, and the black bars usually correspond to when the tutor is speaking.

Date Tutor Minutes % Visualization
2026-06-05 R 26 58%
2026-06-12 R 25 59%
2026-06-16 R 26 59%
2026-06-26 R 29 65%
2026-07-17 R 30 65%
2026-07-31 R 28 59%
2026-08-04 R 34 72%
2026-08-14 R 30 70%
2026-08-21 R 28 62%
2026-09-03 R 32 69%
2026-09-08 C 18 55%
2026-09-18 R 26 61%
2026-09-25 I 21 36%
2026-09-29 I 32 58%
2026-10-09 R 33 69%

One of the things I like about my regular tutor is that he has a technical background (uses Linux and Neovim, experiments with AI), so I enjoy talking about projects and ideas with him. In my conversation classes, he does a good job of asking follow-up questions, suggesting things I could try, and correcting or filling in the occasional thing I'm trying to find a word for in French.

For fun, I decided to make a real-time display of this too: Using Emacs to visualize speaking and target words/phrases in real-ish time. I'm usually too focused on figuring out what to say in French to actually pay attention to that during the conversation itself, but maybe it'll be handy someday.

My next steps could include analyzing the other side of the conversation to find out my average time to respond. People usually start speaking within 200 milliseconds of the end of the other speaker's turn. I take a lot longer because I'm still formulating my thoughts and finding words, but it might be interesting to track my progress.

View Org source for this post

Using Emacs to visualize speaking and target words/phrases in real-ish time

| audio, speech-recognition, emacs, learning, visualization
2026-10-07_07-33-03.png
Figure 1: Screenshot showing my list of target phrases and which ones have been recognized

When I'm working with a tutor to improve my French, I sometimes need to push myself to provide more details when I'm sharing and ask more follow-up questions in response to what they're saying, instead of falling back to « D'accord » and « C'est cool. » Visualizing the timeline in real-time lets me see if the turn-taking looks good. I might even need to take a more active role in controlling the conversation if I happen to be meeting a tutor who tends to go on monologues.

When I'm talking out loud on my own for practice, this might also nudge me to reduce the pauses and enrich my vocabulary by practising saying things often enough that they become more automatic.

1. Voice activity detection and speech recognition

This code uses this server vad-file.py, which I generated with Google Gemini. It emits JSONL for voice activity detection events (start/stop) and uses Orukeet to transcribe the speech once the voice activity detection notices it's finished. I also use spacy to look up token information because I want to someday colour the nouns by gender, which might allow me to correct myself in the next sentence instead of hesitating in the previous one.

code for sacha-vad-tutor
;; Report on the balance between my tutor and me
(defvar sacha-vad-tutor-process nil)
(defvar sacha-vad-tutor-dir "~/vendor/orukeet")
(defvar sacha-vad-tutor-command `(,(expand-file-name ".venv/bin/python" sacha-vad-tutor-dir)
                                  "asr.py"
                                  "--spacy-model"
                                  "fr_dep_news_trf"
                                  "-s" "300" "-t"
                                  "VirtualMicSink.monitor=sacha" "alsa_output.usb-AT_AT2040USB_202011110001-00.analog-stereo.monitor=other"))
(defvar sacha-vad-tutor-events nil "List of events as an list of alists (label start stop duration).")
(defvar sacha-vad-tutor-labels '("sacha" "other"))
(defvar sacha-vad-tutor-summary '(("sacha" . 0)
                                  ("other" . 0)))
(defvar sacha-vad-tutor-session-length (* 60 60) "Number of seconds to use for scale.")

(defun sacha-vad-tutor-update-view ()
  "Visualize the last 3 minutes and the overview of the session.
Also keep track of target phrases."
  (interactive)
  (with-current-buffer (get-buffer-create "*tutor stats*")
    (erase-buffer)
    (let* ((total (apply '+ (mapcar 'cdr sacha-vad-tutor-summary)))
           (height 30)
           (width (window-width nil t))
           (max-seconds
            (if sacha-vad-tutor-events
                (apply 'max (mapcar (lambda (o) (alist-get 'end o))
                                    sacha-vad-tutor-events))
              0))
           (threshold 180)              ; last three minutes
           (threshold-seconds (- max-seconds threshold))
           (recent
            (seq-keep
             (lambda (o)
               (when (>= (alist-get 'start o)
                         threshold-seconds)
                 (let-alist o
                   `((label . ,.label)
                     (start . ,(- .start threshold-seconds))
                     (end . ,(- .end threshold-seconds))))))
             (copy-sequence sacha-vad-tutor-events)))
           (recent-total (apply '+ (mapcar (lambda (o)
                                             (-
                                              (alist-get 'end o)
                                              (alist-get 'start o)))
                                           recent))))
      (save-excursion
        (insert
         (format "Last %d minute%s:\n"
                 (/ threshold 60)
                 (if (> (/ threshold 60) 1)
                     "s"
                   ""))
         (mapconcat
          (lambda (o)
            (format "%d%% %s\n"
                    (/ (* 100.0 (cdr o)) recent-total)
                    (car o)))
          (seq-map
           (lambda (group)
             (cons (car group)
                   (apply '+ (mapcar (lambda (o)
                                       (-
                                        (alist-get 'end o)
                                        (alist-get 'start o)))
                                     (cdr group)))))
           (seq-group-by (lambda (o) (alist-get 'label o)) recent)))
         "\n")
        (svg-insert-image
         (sacha-vad-visualize
          sacha-vad-tutor-labels
          recent
          t
          width height))
        (insert
         "\nTotal:\n"

         (mapconcat
          (lambda (o)
            (format "%d%% %s\n"
                    (/ (* 100.0 (cdr o)) total)
                    (car o)))
          sacha-vad-tutor-summary))
        (svg-insert-image
         (sacha-vad-visualize sacha-vad-tutor-labels sacha-vad-tutor-events t
                              width height
                              sacha-vad-tutor-session-length))
        (insert "\n\n"  (sacha-learn-lang-bingo-summary))))))

(defun sacha-vad-handle-end-event (data)
  "Handle the end event."
  (let ((event (list
                (cons 'label (alist-get 'label data))
                (cons 'start (- (alist-get 'timestamp data)
                                (alist-get 'duration data)
                                (or sacha-vad-tutor-start-seconds 0)))
                (cons 'end (- (alist-get 'timestamp data)
                              (or sacha-vad-tutor-start-seconds 0))))))
    (push event sacha-vad-tutor-events)
    (let-alist data
      (incf (alist-get (alist-get 'label data) sacha-vad-tutor-summary 0 nil 'string=)
            .duration))
    (sacha-vad-tutor-update-view)))

(defvar sacha-vad-event-handler-functions '(sacha-vad-speech-process) "Functions to call with the VAD/transcription events.")

(defvar sacha-vad-process-my-speech nil "Non-nil means process my speech through the whisper setup.")
(defun sacha-vad-speech-process (data)
  "Process DATA through my whisper input process if it's my speech.
Add to `sacha-vad-event-handler-functions'."
  (when (and sacha-vad-process-my-speech
             (string= (alist-get 'event data) "final")
             (string= (alist-get 'label data) sacha-vad-my-label))
    (with-current-buffer (get-buffer-create whisper--stdout-buffer-name)
      (erase-buffer)
      (insert (alist-get 'text data))
      (goto-char (point-min)))
    (whisper--handle-transcription-output nil (get-buffer-create whisper--stdout-buffer-name))))

(defun sacha-vad-tutor-filter (proc string)
  "Add and parse."
  (when (buffer-live-p (process-buffer proc))
    ;; split the string by newline?
    (let ((data (with-current-buffer (process-buffer proc)
                  (goto-char (point-max))
                  (save-excursion
                    (insert string))
                  (catch 'waiting
                    (unless (or (eobp) (looking-at "\n*$"))
                      (json-read))))))
      (run-hook-with-args 'sacha-vad-event-handler-functions data)
      (when (string= (alist-get 'event data) "end")
        (sacha-vad-handle-end-event data))
      (when (string= (alist-get 'event data) "final")
        (sacha-vad-handle-text-event data)))))

(defvar sacha-vad-last-speaker nil "Previous speaker.")

(defvar sacha-vad-transcript-buffer nil "If non-nil, add speech to this buffer.")
(defun sacha-vad-add-to-transcript (text)
  "Add TEXT to the transcript buffer."
  (when sacha-vad-transcript-buffer
    (with-current-buffer (get-buffer-create sacha-vad-transcript-buffer)
      (set (make-local-variable 'window-point-insertion-type) t)
      (save-excursion
        (goto-char (point-max))
        (insert text)))))

(defvar sacha-vad-my-label "sacha")
(defun sacha-vad-handle-text-event (data)
  (sacha-vad-add-to-transcript
   (concat
    (if (string= sacha-vad-last-speaker (alist-get 'label data))
        " "
      (concat (if (bobp) "" "\n\n") (alist-get 'label data) ": "))
    (alist-get 'text data)))
  (setq sacha-vad-last-speaker (alist-get 'label data))
  (when (string= (alist-get 'label data) sacha-vad-my-label)
    (sacha-learn-lang-notice-bingo-words (alist-get 'text data))))

(defun sacha-vad-tutor-start ()
  "Start the process."
  (interactive)
  (sacha-vad-tutor-ensure)
  (setq sacha-vad-tutor-summary '(("sacha" . 0)
                                  ("other" . 0))
        sacha-vad-tutor-events nil)
  (with-current-buffer (get-buffer-create "*Transcript*")
    (erase-buffer)
    (display-buffer (current-buffer)))
  (display-buffer (get-buffer-create "*tutor stats*")))

(defun sacha-vad-tutor-ensure ()
  "Start the process if it's not already running."
  (interactive)
  (unless (process-live-p sacha-vad-tutor-process)
    (setq sacha-vad-tutor-process-start-time (current-time))
    (setq sacha-vad-tutor-start-seconds 0)
    (let ((default-directory sacha-vad-tutor-dir))
      (setq sacha-vad-tutor-process
            (make-process
             :name "vad-tutor"
             :command sacha-vad-tutor-command
             :connection-type 'pipe
             :buffer (get-buffer-create "*vad-tutor*")
             :stderr (get-buffer-create "*vad-tutor-err*")
             :filter #'sacha-vad-tutor-filter)))))

(defun sacha-vad-tutor-stop ()
  "Stop the process."
  (interactive)
  (interrupt-process sacha-vad-tutor-process))

;;;###autoload
(defun sacha-js-parse-jsonl-file (file)
  "Parse jsonl as a list"
  (let (results)
    (with-temp-buffer
      (insert-file-contents file)
      (goto-char (point-min))
      (while (not (eobp))
        (condition-case nil
            (push (json-read) results)
          (error (goto-char (point-max))))))
    (nreverse results)))

(defvar sacha-vad-tutor-start-seconds 0 "Number of seconds to consider as the start.")
(defvar sacha-vad-tutor-process-start-time nil "Time that the process started.")

;;;###autoload
(defun sacha-vad-tutor-reset-clock ()
  "Reset the start of the session."
  (interactive)
  (setq sacha-vad-tutor-start-seconds
        (time-to-seconds (time-subtract (current-time)
                                        sacha-vad-tutor-process-start-time)))
  (mapcar (lambda (o) (setcdr o 0)) sacha-vad-tutor-summary)
  (setq sacha-vad-tutor-events nil))

(defun sacha-vad-visualize (labels list &optional output-file width height max-seconds)
  (setq height (or height 25))
  (setq width (or width (window-width nil t)))
  (let* ((svg (svg-create width height))
         (max-seconds (max
                       (or max-seconds 0)
                       (if list
                           (apply 'max (mapcar (lambda (o) (alist-get 'end o))
                                               list))
                         0)))
         (pixels-per-second (/ (* 1.0 width) max-seconds))
         (row-height (/ height (length labels))))
    (dolist (o list)
      (let-alist o
        (let ((y (* row-height (or (seq-position labels .label 'string=) -1))))
          (when (>= y 0)
            (svg-rectangle
             svg
             (* .start pixels-per-second)
             y
             (* (- .end .start) pixels-per-second)
             row-height :fill "green")))))
    (cond
     ((stringp output-file)
      (with-temp-file output-file
        (svg-print svg)))
     (output-file
      svg)
     (t
      (with-current-buffer (get-buffer-create "*vad*")
        (goto-char (point-min))
        (display-buffer (current-buffer))
        (erase-buffer)
        (svg-insert-image svg))))))

(defun sacha-vad-stats (labels list)
  "Return the balance of time talking as (label total percentage)."
  (let* ((max-seconds (apply '+ (mapcar
                                 (lambda (o) (let-alist o (- .end .start)))
                                 list)))
         (durations (mapcar (lambda (o)
                            (cons
                             (car o)
                             (mapcar (lambda (e) (let-alist e (- .end .start)))
                                     (cdr o))))
                          (seq-group-by (lambda (o) (alist-get 'label o))
                                        list))))
    (seq-map
     (lambda (label)
       (let* ((row (cdr (assoc label durations)))
              (total (apply '+ row)))
         `((label . ,label)
           (total . ,total)
           (count . ,(length row))
           (percentage . ,(and total max-seconds (> max-seconds 0) (* 100.0 (/ total max-seconds)))))))
     labels)))

;; (sacha-vad-stats '("sacha" "tutor") (seq-take (sacha-js-parse-jsonl-file "~/proj/speech/output2.log") 5))
;; (sacha-vad-stats '("sacha" "tutor") (sacha-js-parse-jsonl-file "~/proj/speech/output.log"))
;; (sacha-vad-visualize '("sacha" "tutor") (sacha-js-parse-jsonl-file "~/proj/speech/output.log"))
;; (sacha-vad-visualize '("sacha" "tutor") (seq-take (sacha-js-parse-jsonl-file "~/proj/speech/output2.log") 30))

(defun sacha-vad-tutor-group-output (files)
  (let ((grouped-files (seq-group-by
                        (lambda (o)
                          (substring (file-name-base o) 0 10))
                        files)))
    (mapcar
     (lambda (group)
       (let ((start 0))
         (cons (car (cdr group))
               (seq-reduce
                (lambda (prev file)
                  (append prev
                          (let ((max-seconds 0))
                            (prog1 (mapcar (lambda (event)
                                             (incf (alist-get 'start event) start)
                                             (incf (alist-get 'end event) start)
                                             (setq max-seconds (max (alist-get 'end event) max-seconds))
                                             event)
                                           (sacha-js-parse-jsonl-file file))
                              (setq start max-seconds)))))
                (cdr group)
                nil))))
     grouped-files)))

(defun sacha-vad-tutor-make-table (files &optional width height)
  "Make a summary table."
  (let ((labels sacha-vad-tutor-labels)
        (width (or width 500))
        (height (or height 30)))
    ;; combine data as needed
    (append
     '(("Date" "Tutor" "Minutes" "%" "Visualization") hline)
     (seq-map
      (lambda (group)
        (let* ((list (cdr group))
               (base (file-name-base (car group))))
          (if list
              (let-alist (car (sacha-vad-stats labels list))
                (let ((svg-name
                       (expand-file-name
                        (concat (file-name-base (car group)) "-" .label ".svg")
                        "~/proj/french/conversation")))
                  (sacha-vad-visualize
                   (list .label)
                   list
                   svg-name
                   width
                   height)
                  (list
                   (format-time-string "%Y-%m-%d"
                                       (seconds-to-time
                                        (sacha-filename-timestamp (car group))))
                   ;; (org-link-make-string (concat "file:" o) "file")
                   (if (string-match "-conversation-\\([^\\.]+\\)" base)
                       (s-capitalize (substring (match-string 1 base) 0 1))
                     "")

                   (round (/ .total 60))
                   (format "%d%%" .percentage)
                   (org-link-make-string (concat "file:" svg-name)
                                         (concat "file:" svg-name))
                   )))
            (list "?"))))
      (sacha-vad-tutor-group-output files))
     nil)))

2. Noticing bingo phrases

Here's the code that reminds me to use the words or expressions on my to-do list, and then keeps track of how often I've used them, prioritizes the ones that I haven't used yet, and displays these. It can work with either my Whisper dictation workflow or the VAD events server described above.

I keep the list of expressions in file:///home/sacha/proj/french/bingo.txt.

Code to notice when I've used an expression
(defvar sacha-learn-lang-bingo-word-file "~/proj/french/bingo.txt")
(defvar sacha-learn-lang-bingo-words nil "List of words.")
(defvar sacha-learn-lang-bingo-words-hash nil "Data for the counts.")

;;;###autoload
(defun sacha-learn-lang-bingo-words ()
  "Load the list of words from the file."
  (interactive)
  (setq sacha-learn-lang-bingo-words
        (seq-map (lambda (o)
                   (when (= (length o) 1)
                     (setq o (list (car o) 0)))
                   (setq o (append o (list
                                      (string-trim (replace-regexp-in-string
                                                    " *\\[.+?\\] *" " "
                                                    (car o))))))
                   o)
                 (pcsv-parse-file sacha-learn-lang-bingo-word-file)))
  sacha-learn-lang-bingo-words)

;;;###autoload
(defun sacha-learn-lang-bingo-handler (event)
  "Manually register the event."
  (interactive "e")
    (let* ((pos (posn-point (event-end event)))
           (val (get-text-property pos 'bingo)))
      (when val
        (sacha-learn-lang-notice-bingo-words val))))

(defvar-keymap sacha-learn-lang-bingo-map
  "<mouse-1>" #'sacha-learn-lang-bingo-handler)

;;;###autoload
(defun sacha-learn-lang-bingo-summary ()
  "Summarize the words to say."
  (let ((target (seq-filter (lambda (row) (= (elt row 1) 0))
                            sacha-learn-lang-bingo-words))
        (yay (seq-filter (lambda (row) (> (elt row 1) 0))
                         sacha-learn-lang-bingo-words)))
    (concat
     (if target
         (concat "Target: "
                 (mapconcat (lambda (row)
                              (propertize
                               (car row)
                               'keymap sacha-learn-lang-bingo-map
                               'bingo (car row)))
                            target
                            "; ")
                 "\n\n")
       "")
     (if yay
         (concat
          "Yay! "
          (mapconcat (lambda (row)
                       (propertize (format "%s (%s)" (car row) (elt row 1))
                                   'keymap sacha-learn-lang-bingo-map
                                   'bingo (car row)))
                     (seq-filter (lambda (row) (> (elt row 1) 0))
                                 sacha-learn-lang-bingo-words)
                     "; ")
          "\n\n")
       ""))))

;;;###autoload
(defun sacha-learn-lang-notice-bingo-words (text)
  "Look for the bingo words in TEXT and celebrate when I use them.
Add to `whisper-insert-text-at-point'."
  (when text
    (unless sacha-learn-lang-bingo-words (sacha-learn-lang-bingo-words))
    (let ((found (seq-find
                  (lambda (o)
                    (and (elt o 2)
                         (string-match (concat "\\<" (elt o 2)) text)))
                  sacha-learn-lang-bingo-words)))
      (when found
        (incf (elt found 1))
        (sacha-vad-tutor-update-view)
        (with-current-buffer (get-buffer-create "*Bingo*")
          (erase-buffer)
          (insert (sacha-learn-lang-bingo-summary))))))
  text)
This is part of my EmacsConfiguration.
View Org source for this post

Looking at landscapes; art and iteration

| art, supernote, learning, education

I want to get better at helping A+ learn more about art, and I want to learn more about art myself. She'll learn whatever she's ready to learn, but maybe I can help her get past the initial frustrations by breaking it down into smaller skills. As for me, there's plenty I can learn about seeing, getting stuff to look like what I'm seeing, imagining things, and communicating them. If I go about learning the things I want to learn, maybe she'll come along and pick things up too.

A+'s grade 3 virtual teacher assigned a landscape art project focusing on depth (foreground, middle ground, background) and value (highlight, midtone, shadow) using images from The Hidden Worlds of the National Parks. A+ was curious about Glacier National Park, and following that thread led us to this photograph of Saint-Mary Lake by Angelo Chiacchio (2018), so we used it as a reference. Angelo Chiacchio took this picture during a 300-day solo journey focusing on the precarity of our relationship with the world around us. He called this project .

Anyway. Back to the assignment. When A+ started her artwork in Procreate the other day, I noticed she was getting frustrated with her lines and curves not going where she wanted them to go. I suggested approaching it as a painting instead, blocking in masses of colour (… am I even using these words correctly?) and then gradually refining them based on what she sees, kind of like how you can smoosh some clay and then push it around until it feels right. She liked that approach better. We talked about fractions as we figured out how much space the background features took, and she painted land and sky and land and sky until things felt right to her. As she added details, I sometimes mentioned things I saw in the photo that I was trying to add to my painting, and she figured out her own interpretations of those. I liked how we both got the foreground/middle ground/background distinction using size and detail, and how the shadows helped the rocks look like they were part of the landscape.

Here's my take on it. Not entirely sure about the derivative work status of these ones, but I'm fairly sure they're no threat to Angelo Chiacchio's professional prospects as a designer/photographer/filmmaker. The first one is done using the Atelier drawing mode on my Supernote A5X, and the second one using the regular note app on the Supernote and just white/black:

Now that I've had a chance to look at the reference photo on my external monitor instead of on my phone screen, I can see a few more details, like peaks behind the forests on the left side. Working with just black/white is handy as I don't need to slow down to change pen colours. Maybe I can experiment with a midtone background so that I just need to add white and black.

Yesterday, we logged off from virtual school early to go to the Art Gallery of Ontario. I knew the class was going to do some more work on landscape art, so I figured it might be nice to check out the gallery and see things at a different scale. We could look at actual landscape paintings. As we wandered through the galleries, A+ was particularly interested in the Lawren S. Harris paintings like South Shore, Bylot Island, which had two other variations:

We looked at the foam on the waves, the contrast of the mountains, the clouds, the light, the shape of the peaks and the level of detail, the overlapping of the ridges of the mountains, the proportion of water to land to sky. She pointed to the elements of the paintings and looked closely at how it was put together.

By Lawren S. Harris, paintings from https://ago.ca, all rights reserved:

bylot-island-shore-sketch-32.jpg
Figure 1: Sketch XXXII
bylot-island-shore-sketch-35.jpg
Figure 2: Sketch XXXIV
south-shore-bylot-island.jpg
Figure 3: South Shore, Bylot Island

(I think it's okay to use these thumbnails under the Fair Dealing clause of AGO Terms of Use.)

Reading more about Lawren S. Harris, I learned that he invited artists to come together, provided them an inexpensive space to work, and financed trips for them, and helped form the Group of Seven (of which he was one) in 1920. That reminds me a little of William Thurston's thoughts on how mathematical knowledge can move so much more quickly through informal, in-person discussions compared to lectures or published papers. Connection: A group of painters thinking about Canadian art together. And a small-scale connection: the bouncing around of ideas in the Emacs community. But I am trying to squeeze too many tangents into this post.

I liked being able to look at versions of the same idea and discuss the differences between them. Today I looked up the paintings so I could write about them. I told A+ about how the two sketches were numbered #32 and #35, which means the artist probably did lots of studies to figure out how to paint what he wanted to show, and that even accomplished artists try lots of things in order to figure things out. It's interesting to get a glimpse of what happens behind the scenes of a polished piece of art.

I brought the iPad and my Supernote so that A+ could finish her digital landscape painting and so that I could work on mine. A proper class field trip came in, too. We watched the grade 6/7 students sprawl on the floor, pick paintings to study, and sketch with pencil and paper. A+ got her painting to a point where she really liked it. I liked the way her digital brushstrokes textured the rocks in the foreground where mine still felt flat, and the attention she paid to the snow in the peaks. Anyway, homework done, we explored some more. She found the AGO energizing and pulled me from exhibit to exhibit, although we did have to reluctantly save some galleries for the next trip.

I was a little envious of A+'s familiarity with Procreate. Maybe when I get the hang of value and if art becomes more of a thing, I might consider getting my own iPad for digital painting, since she often uses W-'s iPad for reading, watching, or drawing. I'd love to work with colours again. In the meantime, I still have much I can learn on the Supernote, even though it can only do white, black, and two levels of gray. When I browse through /r/supernote for inspiration (there's a filter for just artwork posts), it's… ah… easy to see that the hardware is not the limiting factor. Besides, I can practise using Krita on the X230 tablet PC. And it's been helpful, actually, limiting myself to just what the Supernote can do. I don't have to spend time trying to figure out colours that reflect what I see and that somehow work together with the other colours in the image. I can focus on learning how to see in terms of value first, and maybe dig into more of the techniques around black and white drawings.

Towards the end of my father's life, he took up drawing and watercolour painting, teaching himself through YouTube tutorials and tons of practice. As an advertising photographer, he had already spent decades thinking about composition and light, so I think he had a bit of an unfair advantage, especially since drawing meant that he didn't even have to have the right dramatic sky to Photoshop into an image.

When my dad asked me which of his drawings or paintings I wanted to keep, I asked for his sketchbook. I wanted the rough sketches, the in-between steps, the experiments. He gave me his one sketchbook and a bunch of loose sketches in a small case. I think he must have drawn in other sketchbooks, but maybe he didn't keep them, or maybe he really just leveled up that quickly. So here's a series of sketches by John K. Chua (all rights reserved). I'm pretty sure he was following this tutorial on How to Draw a Lighthouse, the Sea and Sky, but I'm just guessing at the sequence of these sketches.

This was about half a year before his death. Cancer meant he couldn't get out as much as he used to, so he had to channel his passion for photography and learning into something else. It's interesting to see him experiment with the shapes in the sky, the contrast and shape of the shore, the rocks, the light from the lighthouse. He made many other sketches and paintings, often with several variations in the sketchbook. It would have been nice to see what he could've done with years more experimentation, but ah well.

While reading about art studies and iteration, I came across these posts:

So yes, definitely a thing.

I've been having fun drawing more. I could pick a tutorial, a Creative Commons image, or a public domain image as a reference so I can freely share my iterations. It'll be interesting to do that kind of iteration. I'm not sure A+'s at the point of being able to do that kind of study yet. I'm not totally sure I'm at that point yet either. My mind is often pulled in other directions by ideas and novelty. I am definitely going to lose her if I insist she repeats things.

That reminds me a little of another reflection I've been noodling around on interest development. The article Enhance Your Reference Skills by Knowing the Four Phases of Interest Development and this presentation mention that in the phase of emerging personal interest, when people are starting to become curious and independently re-engage a topic, they're not particularly interested in being advised on how to improve what they've currently got. It's better to acknowledge the effort they're putting in and to be patient. So I might as well just learn beside her, experimenting on my own stuff, letting her peek in, and see where that takes us.

This is hard. But life is long (generally), and she can learn things when she's ready. She can only learn things when she's ready. There's time. I didn't grow up particularly confident in art. I still mostly draw stick figures. But to my great surprise, I've managed to get paid for a few of them as a grown-up, and I use them myself to think and grow. Sometimes I discover myself drawing for fun.

At 41 years, what am I ready to learn about art? About life?

I have that sense of discrepancy between my clumsy lines and blobs and actions, and the shapes and results I want. This is good. I can imagine that there's something better, even if that's often unclear, and it's not… whatever this is. That is the gap between taste and skill that Ira Glass described.

Nobody tells this to people who are beginners, I wish someone told me. All of us who do creative work, we get into it because we have good taste. But there is this gap. For the first couple years you make stuff, it’s just not that good. It’s trying to be good, it has potential, but it’s not. But your taste, the thing that got you into the game, is still killer. And your taste is why your work disappoints you. A lot of people never get past this phase, they quit. Most people I know who do interesting, creative work went through years of this. We know our work doesn’t have this special thing that we want it to have. We all go through this. And if you are just starting out or you are still in this phase, you gotta know its normal and the most important thing you can do is do a lot of work. Put yourself on a deadline so that every week you will finish one story. It is only by going through a volume of work that you will close that gap, and your work will be as good as your ambitions. And I took longer to figure out how to do this than anyone I’ve ever met. It’s gonna take awhile. It’s normal to take awhile. You’ve just gotta fight your way through.

With any luck, I'm never going to outrun the gap. An important part to learn (and share) is how to let go of the frustration and self-doubt that get in the way, so that we can get on with the learning. That's hard. I am learning to experiment, even if it looks like I'm only changing a little bit at a time, and even if I often go sideways or backwards more than forward. I am trying to get better at sketching and taking notes so that I can see things side by side. In life, part of the challenge is figuring out the characteristics of this quirky medium–what it permits at this particular moment. I just have to keep trying, and observing, and thinking, and changing; not quite the same thing again and again.

View org source for this post

How to Take Smart Notes - Sonke Ahrens (2017)

| visual-book-notes, writing, pkm, productivity, learning

I want to get better at making sense of things and sharing what I'm learning. Nudged by Chris Maiorana's post on Second Brain, Second Nature, I borrowed How to Take Smart Notes by Sönke Ahrens (2017). Here are my notes.

Text from sketch

How to Take Smart Notes - Sönke Ahrens. 2017 - sketched by Sacha Chua 2024-10-26-01

  • Niklas Luhmann: everything - writing; slipbox, Zettelkasten
  • Instead of: brainstorm (blank paper), then research (wrong topic? wrong understanding?), then write
  • Try a loop of:
    • Read with a pen in hand: short notes, your own understanding
    • Refine and connect your notes: elaborate.
    • Notice clusters
    • Develop into topics, write about them
    • reading ⇒ thinking ⇒writing
  • Types of notes
    • Fleeting: try to review within a day
    • Permanent: complete sentences, makes sense at a glance
    • Literature: short; use own words
    • Project: can be archived after
  • Work on multiple projects so you can switch between them and they can feed each other.
  • Things to think about.
    • Why is this interesting?
    • Why is this relevant?
    • How does this relate to other things?
    • What's not mentioned?
  • Numbering, physical references: let ideas mingle
    • 22, 22a, 22a1, 22b, 23, …
  • Retrieval cues
  • Saving cut pieces = easier editing
  • Verbund: by-products = resources
  • Writing → break it up!
    • reading, understanding, reflecting, getting ideas, connecting, distinguishing, rewording, structuring, organizing, editing, rewriting
  • Positive feedback loop: reading with pen, writing permanent notes, writing arguments…

The book goes into detail about Niklas Luhmann's Zettelkasten or slipbox system. Lots of people have written about Zettelkasten and various implementations. There's even a whole micro-industry around Notion templates. So I won't spend a lot of time right now describing what it is or what the key aspects are. I can focus instead on what that means to me and what I want to do with it.

Writing

By doing everything with the clear purpose of writing about it, you will do what you do deliberately.

I like chapter 5's focus on keeping writing in mind. I want to push most things towards writing and drawing (posts, code, whatever; public as much as possible) because it's a good way for me to remember and to learn from others. It's a reminder to not try speeding through my to-do list; it's good to slow down and write about stuff.

Following the work

I only do what is easy. I only write when I immediately know how to do it. If I falter for a moment, I put the matter aside and do something else.

I always work on different manuscripts at the same time. With this method, to work on different things simultaneously, I never encounter any mental blockages.

During my discretionary time, I usually follow the butterflies of my interest: working on what I feel like working on, moving on to something else when I get stuck. Sometimes I will work on something I have to do because it's got to be done, but those moments are rarer. Amidst all those productivity books that exhort you to focus on a limited number of things, it was nice to know that Luhmann also jumped from interest to interest, that the process of accumulating these notes builds things up into clusters with critical mass, and that these good habits build themselves up through positive feedback loops.

Different types of notes

I do all right capturing fleeting notes on my phone, but I want to get better at turning my fleeting notes into literature notes and permanent notes. I'd like to review them more frequently and spend some more time fleshing them out, with the goal of eventually turning more of those things into blog posts and code that I can share as I learn out loud.

I also don't really have a good way of putting topics "near" other topics yet. Categories are a little coarse, but maybe topic maps are a good starting point. It would be nice to have a quick way to put something before/after something else, though.

Different types of tasks

Writing a paper involves much more than just typing on the keyboard. It also means reading, understanding, reflecting, getting ideas, making connections, distinguishing terms, finding the right words, structuring, organizing, editing, correcting and rewriting.

I wonder if making these distinctions between the subtasks of writing will make it easier for me to break writing down into tiny tasks that can be completed and gotten out of my brain.

Thinking about connections, thinking about what's missing

I want to get better at connecting ideas to other things I've thought about by linking to blog posts or notes. That might also help me build up thoughts out of smaller chunks, which would be helpful when it comes to working with fragmented thoughts.

Thinking about what's not in the picture is hard, and that kind of critical thinking is something I want to practise more. I can pay attention to the follow-up questions I have so that I can get a sense of where to look for more insights or what to experiment with. Questioning the way something is framed is also good and something I don't do often enough.

For example, I wanted to dig into this quote:

Luhmann’s only real help was a housekeeper who cooked for him and his children during the week, not that extraordinary considering he had to raise three children on his own after his wife died early.

I ended up doing a tiny bit of research on my phone and putting it into Niklas Luhmann's Zettelkasten and life with kids (the kids were in their teens at the time, so they were probably a lot more independent than A+ is at the moment).

Related

View org source for this post

Working with the flow of ideas

| speechtotext, metaphor, life, blogging, writing, kaizen

Text from sketch

2023-12-25-07

Flow of ideas

What can I learn from thinking about the flow rate?

input > output, and that's okay

Parts:

  • idea: agenda/review?
  • capture: refile to tags
  • toot: use this more, get stuff out
  • braindump: use transcripts or outline
  • sketch: bedtime
  • post: cut off earlier, can follow up
  • video: workflow tweaks

Thoughts:

  • more input is not always better; already plenty, not limiting factor
  • prioritize, review
  • overflow: add notes and pass it along, if poss.
  • can add things later (results, sketches, posts, videos)
  • manage expectations; minimize commitments
  • favour small things that flow easily
  • collect things in a container
    • tags, outlines
    • posts, videos
  • minimize filing, but still find related notes
  • become more efficient and effective

The heap:

  • Org dates have been working for time-sensitive/urgent things
  • Lots of discretionary things get lost in the shuffle
    • waste info collected but forgotten
    • half-finished posts that have gone stale
    • redoing things
    • late replies to conversations
    • things that are just in my config - some people still find them, so that's fine

Next: toot more experiment with braindumping, video

I come up with way more ideas than I can work on, and that's okay. That's good. It means I can always skim the top for interesting things, and it's fine if things overflow as long as the important stuff stays in the funnel. I'm experimenting with more ways to keep things flowing.

I usually come up with lots of ideas and then revisit my priorities to see if I can figure out 1-3 things I'd like to work on for my next focused time sessions. These priorities are actually pretty stable for the most part, but sometimes an idea jumps the queue and that's okay.

There's a loose net of projects/tasks that I'm currently working on and things I'm currently interested in, so I want to connect ideas and resources to those if I can. If they aren't connected, or if they're low-priority and I probably won't get to them any time soon, it can make a lot of sense to add quick notes and pass it along.

For things I want to think about some more, my audio braindumping workflow seems to be working out as a way to capture lots of text even when I'm away from my computer. I also have a bit more time to sketch while waiting for the kiddo to get ready for bed. I can use the sketchnotes as outlines to talk through while I braindump, and I can take my braindumps and distill them into sketches. Then I can take those and put them into blog posts. Instead of getting tempted to add more and more to a blog post (just one more idea, really!), I can try wrapping up earlier since I can always add a follow-up post. For some things, making a video might be worthwhile, so smoothing out my workflow for creating a video could be useful. I don't want to spend a lot of time filing but I still want to be able to find related notes, so automatically refiling based on tags (or possibly suggesting refile targets based on vector similarity?) might help me shift things out of my inbox.

I'm generally not bothered by the waste of coming up with ideas that I don't get around to, since it's more like daydreaming or fun. I sometimes get a little frustrated when I want to find an interesting resource I remember coming across some time ago and I can't find it with the words I'm looking for. Building more of a habit of capturing interesting resources in my Org files and using my own words in the notes will help while I wait for personal search engines to get better. I'm a little slow when it comes to e-mails because I tend to wait until I'm at my computer–and then when I'm at my computer, I prefer to tinker or write. I occasionally redo things because I didn't have notes from the previous solution or I couldn't find my notes. That's fine too. I can get better at taking notes and finding them.

So I think some next steps for me are:

  • Post more toots on @sachac@emacs.ch; might be useful as a firehose for ideas. Share them back to my Org file so I have a link to the discussion (if any). Could be a quick way to see if anyone already knows of related packages/code or if anyone might have the same itch.
  • See if I can improve my braindumping/sketch workflow so that I can flesh out more ideas
  • Tweak my video process gradually so that I can include more screenshots and maybe eventually longer explanations

Revisiting stenography and Twiddling

| learning, steno, twiddler, geek

I've been thinking about what I can learn alongside A+ - something that will be slow to learn but that might be fun to get the hang of. I think it will help me practise patience and develop my empathy with her as she learns fine motor skills too. Maybe I'll even be able to model persistence and self-acceptance.

At home, I practise steno on my Georgi keyboard while she does her homework, learns how to type, or writes stories. It works out pretty well, since I have to sound out words to chord them and I fingerspell slower than she types. I steno the words she wants me to spell for her, and I also steno the instructions on her homework. I steno my journal entries, too. I'd like to someday be able to write blog posts with steno. Not that I'm speed-limited now, but I'm curious about it and it's good to show A+ that I'm learning too. I made a webpage that lets me steno large text into a small textarea on my Android phone using Dotterel, displaying my cheat sheet and steno hints for the last few words using the main typeytype dictionary. That way, I can fingerspell words for A+ and then practise them as she copies the words. I'm also slowly going through The Art of Chording.

I've also dusted off my Twiddler 2 one-handed chording keyboard, since that's something I can do while looking elsewhere. Looking outside makes me feel a little happier in winter. Maybe I'll even figure out how to write while waiting for her outside, perhaps bundling up my hand in a small blanket to keep warm. I mostly remembered how to chord with my right hand using the default layout, but I wanted to experiment with alternative layouts. I started learning a modified Backspice layout, moving some letters around since I can't easily reach 000L with my short pinky. I couldn't download the Twiddler 2.1 configuration tool from the Tekgear website, so I just programmed it interactively.

To practice on the go, I set up Emacspeak in a Debian instance on UserLAnd on my Android phone, with audio output routed using pulseaudio to XServer XSDL via export PULSE_SERVER=tcp:127.0.0.1:4713 in my UserLAnd ~/.profile. It worked surprisingly well. I could press chords and hear what letter I typed. When I pressed SPC, I heard the word read out. This was enough for me to be able to explore the layout and think of words I want to spell with the letters I've found so far. I've been having a hard time figuring out how to easily get files in and out of UserLAnd aside from scp, though, as the document provider doesn't seem to show up for me.

So I wrote a web-based tool that uses the Javascript Web Speech API to speak each letter as I type it and speak out the word after I press space–basically, the main things that I'd been using Emacspeak for. I also added a little cheat sheet that I could update on the fly, and I can have it read aloud by typing hlp and pressing SPC. I like this more self-directed, exploratory approach to learning the keymap. I press a chord and hear what letter it is, then think of words I want to spell with it and where those letters are. Here it is: twiddler.html (might not work on all browsers - I use it on Chrome on Android)

Based on conversations on Mastodon, I decided to get the Twiddler 3 Wrap + Bluetooth. I wonder if the Twiddler 3 will make it easier for me to reach the far button with my pinky finger, and I'm curious if Bluetooth can still get through however many layers I want to have so that I don't get too cold. I probably won't use the Twiddler to write stuff while ostensibly standing around with the other grown-ups at a playdate. It's good for kids to see grown-ups being friends. But if there's standing-around time while she's off being independent, or if I want to look outside, it might be interesting to use.

A+ sometimes gets frustrated with how slow writing is, or how she gets tripped up by a 3x3 perm she wants to learn. I'm glad I can slow down and learn something along with her.

Reflecting on wasted effort

| kaizen

One way to look for ways to improve is to think about where the waste might be. I wanted to reflect on how I'm currently doing things and where I might be wasting effort.

  • Not noticing an opportunity: There's not noticing that there's an opportunity to improve or not seeing that something that I can do that takes advantages of something I'm already doing.
  • Working on the wrong thing: If I pick something less effective to work on, I waste a little opportunity. Something might be a bad fit if it bumps into my weaknesses or doesn't take advantage of my strengths. Maybe I'm picking the wrong problem to work on, or I'm taking the wrong approach, or I haven't prepared, or I'm working on something that may be high effort and low reward. It's usually not a big deal, but it helps to think a little bit about which tasks can lead to compounding benefits and which are one-offs that don't help as much.
  • Working at the wrong time: I feel a little slower working on something when I'm not in the right mindset or I'm not as interested in it as in other things I could be doing. It's also tough when I don't have enough energy to work on things. It's important to notice when I'm getting into the negative productivity zone, especially when coding. If I pick the wrong time to work on something, I might have to deal with lots of interruptions or distractions.
  • Context-switching: Context-switching is a particularly big challenge for me because I'm basically working with one to two hour chunks possibly separated by days or months. For example, if I start something on Tuesday and then I pick it up again on Friday, I need to do a fair bit of rethinking and remembering. Switching from one thing to another is hard. I'm always looking up how to do something in the specific language that I need to work with. It's related to the problem of…
  • Duplicate research: Sometimes I have to reread the resources that would help me prepare for that task.
  • Tunnel vision: On the flip side, focusing too much on one project means not thinking about other things. Everything else tends to be in the back burner because of context switching costs, and that sometimes leads to…
  • Letting an opportunity lapse: Sometimes it's too late to get the most out of something because a person who wanted it has moved on (including me) or because I'd completely forgotten the context of my notes. This also applies to real life, too. A- is not going to want to hang out with me forever, so I should make the most of it. =)
  • Forgetting the context: Quick notes are sometimes too quick.
  • Fragmented time: Since I need to work in short bursts, I have to get to a good stopping point. That can be tough.
  • Frittering away time on distractions: It can also be tough working on something that doesn't fit into five minutes here, five minutes there. There's a big temptation to fritter time away on distractions like scrolling through Reddit, or just working on small, easy stuff instead of thinking about the harder problems.
  • Repetitive steps that could be automated: Waste could also be working on things that the computer could be working on instead.
  • Not making the most of it: If I'm not paying attention, I might not get as much out of an experience or task as I could have.
  • Not harvesting notes/code: It's very tempting sometimes to try to work quickly and just solve the problem for today. But if I take a little bit of extra time to harvest my notes from it, then I might be able to solve that problem when I run into the same problem, six months later or something like that.
  • Doing more than needed: The principle of You Ain't Gonna Need It often comes into play here, especially if I need to squeeze things down to fit into the chunks of time I have.
  • Missing pieces, incomplete notes: If I write something incomplete, I might have to redo more of it when I want to reuse it or build on it.
  • Forgetting where to find something: If I can't even remember the keywords needed to find something, that's even more of a waste of good notes.
  • Mistakes: Mistakes happen, and that's another source of wasted effort. If I'm in a rush or if I'm being impatient, I am not very good at paying attention to details. Then, when I need to go back and fix things, I have to deal with the context-switching all over again.
  • Doing things that might be a better fit for other people: So if there's something that can be done by somebody who's more detail-oriented or who has more time to look at all the small things or who has those particular skills or interests, it's better for them to do it. Then I can focus on the stuff that fits me.
  • Limits of tools: If I'm coding, doing it on my laptop with maybe two side-by-side windows is not quite as effective as plugging into the external monitor and getting all the things set up so I can see things instead of switching between overlapping things.
  • Having things in a form that's hard to search or skim: Videos and sketches can be hard to search or skim, so sometimes it makes sense to go back and actually write the text for it.
  • Negative feelings: For example, if the kiddo really wants my attention and I'm trying to complete a thought, it's tough not to get frustrated by the interruptions. It helps to be able to pull myself back and actually focus on her because there's no getting around that anyway, and then to do my thing later. It's also good to not let that frustration linger, because then that gets in the way of both enjoying her company and being able to focus on my own thing afterwards.

Now to think a little more closely about my main challenges…

Dealing with the fragmentation of my time is a big challenge. The way that I might do that is by grouping tasks together, so I don't have to switch context so much. Tunnel vision hasn't been too much of a problem for me so far, although it does mean that some things don't get worked on for a long time.

Taking a little bit of extra time to write up my notes makes sense, although it means my chunks of coding time have to be even shorter. Extracting excerpts for literate programming posts is a bit tough if I need to think about how to provide enough context. Maybe I should let myself fill things in later. I'm also looking into ways to do that faster, like maybe auto-generated captions running in the background so I can think out loud, grab the transcript, and then edit it a little bit (like this post). We'll see how that goes.

It makes sense to invest some time into expanding my toolset, like learning more about my tools, automating things, or taking advantage of hardware or software.

Of course, I'm still probably going to run into mistakes along the way, but if I can figure out which things are not as good for me, then I can see if other people want to go pick them up.

Might be a reasonable plan for reducing waste. Let's see how it works out.