When defining a new face, how to blend/interpolate between two existing faces colors?

For example - mix the colors foreground of default and the background of the error face with a factor 0.0 being all of the first color, 1.0 returning all of the second.

  • 2
    Please clarify in the question what you mean by blending face colors, i.e., what you're requesting. As it is, you posed an unclear question and then answered it, providing real understanding of what you were requesting only in the answer (the code). The question should describe what you want (better).
    – Drew
    Commented Oct 23, 2021 at 16:47
  • Updated the question.
    – ideasman42
    Commented Oct 24, 2021 at 5:03
  • 1
    What do you mean by "blend"?
    – NickD
    Commented Oct 24, 2021 at 13:31
  • I don't think there's a built in function for this. The closest I see is color-gradient which can construct multiple in-between steps. For example if you wanted a color 0.1 from the foreground / 0.9 from the background you could call (color-gradient foreground background 9) and it would return the 9 in-between colors at 0.1, 0.2, …, 0.9.
    – amitp
    Commented Oct 25, 2021 at 4:25
  • This issue with gradient is it creates a list of colors, which is overkill when only one color is needed.
    – ideasman42
    Commented Oct 25, 2021 at 4:31

1 Answer 1


This macro blends colors between faces, example:

(defun my-face-color-blend (color-a color-b factor)
  "Tint between COLOR-A and COLOR-B by FACTOR in (0..1).
Use for mixing face colors."
  (let ((value-a (color-values color-a))
        (value-b (color-values color-b))
        (ifactor (- 1.0 factor)))
    (apply 'format
             (lambda (n)
               ;; Shift by -8 to map the value returned by `color values':
               ;; 0..65535 to 0..255 for `#RRGGBB` string formatting.
               (ash (truncate (+ (* (nth n value-a) ifactor)
                                 (* (nth n value-b) factor))) -8))
             (number-sequence 0 2))))))

(defface my-half-background-face
  (list (list t (list :foreground
                       (face-attribute 'default :foreground)
                       (face-attribute 'error   :background)
  "Half the background and foreground of the default font.")
  • Any reason this needs to be a macro rather than a function?
    – Dan
    Commented Oct 23, 2021 at 23:18
  • Good point, it should be a function (done).
    – ideasman42
    Commented Oct 24, 2021 at 7:07
  • I'd let-bind color-a and color-b inside the function, and use different names from the parameters. And after staring at it for a couple of minutes, I still don't understand the shift. Part of it might be that I don't understand what you mean by "blend" (as I asked in my comment to your question), so I'm left with reverse-engineering the code to figure that out.
    – NickD
    Commented Oct 24, 2021 at 17:39
  • 1
    Use let binding, clarified blend in the OP.
    – ideasman42
    Commented Oct 25, 2021 at 4:31
  • 1
    Added an explanation for why the shift is there.
    – ideasman42
    Commented Oct 25, 2021 at 7:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.