Perceived Brightness Dimming

Hi all,
I don’t know if this is interesting for anyone else, but I have long been struggling with the actual perceived brightness of a light versus the dimming setting. So I made a simple spreadsheet that allows me to calculate the settings so that each step is equal in brightness as perceived by the average person instead of just a linear setting. I used a standard visual perception model.

For example most LED fixtures have a linear output with respect to input, but the human eye certainly does not. So if we have 255 level settings in homeassistant and we want 16 steps of brightness that all seem to be equal in size, we need levels that change more as the light gets brighter due to the eye becoming less sensitive to brightness change as it gets brighter. In other words, the eye is a non-linear sensor – which is great because we can see across huge range.

If there is any interest I will be glad to clean up my spreadsheet and upload it. It lets you set the desired number of perception steps, the minimum power for the first step, and gives an output based on the scale you tell it (e.g., 0-100 for %, or 0-255 for homeassistant)

A quick set of levels to get 16 levels from a 0-255 setting, and limiting the lowest to 6% power (since most LEDs begin to flicker or go out below that) you get the following:

Level Num. Level Setting
1 0
2 20
3 27
4 36
5 45
6 56
7 68
8 82
9 98
10 115
11 134
12 154
13 177
14 201
15 227
16 255

Blockquote

2 Likes

I was just looking for something like this. non-linear dimming values.

Just like a stereo works with decibels instead of watts

Glad to help. The above numbers are for most LED lights. If you need the ones for incandescent they are:
0
16
21
26
32
39
48
59
72
87
105
127
152
181
215
255
Compact Fluorescent don’t dim very well, but their resulting curve is not far from the incandescent curve.

@txNgineer I like your solution, but how do you configure a light to follow the custom scale? A light template? I have a Tasmota dimmer controlled via MQTT.

I am still learning about Homeassistant templates and am not sure how you would implement this yet. What I have done is use it explicitly in actions for automations. For example to dim a light to 50% perceived brightness when a command is triggered.
In my case I have a spotlight on a piece of art and I turn on the light 100% at sundown, then after 1.5 hrs I drop it to a perceived 50% brightness. I then cut it to 20% near bedtime. For each of those actions I just input the numbers from the above chart.

OK, thanks. I hoped there was an easy way to implement the lookup table that I couldn’t see…

I will update if I found a way. Thanks for answering :blush:

Hi. Do you still have your spreadsheet?
I would like to do something similar and your work could be handy

Sorry for the delayed response. I just now saw this as I was out of commission the last few months of 2020.
Yes I have the spreadsheet, but when I looked back at it a few months ago I realized I was using it to determine perceived brightness based dimmer settings and it did not have a way to “go the other direction” and input various brightnesses and get back results. I began to clean it up and had it mostly fixed to do both types of calculations. But as I mentioned, I got sidetracked and didn’t finish the cleanup.
If you are still interested in a copy, just reply and I will post what I have (with “to be done” notes) on my github. Thanks.

No worries. This is all on a voluntary basis :slight_smile:
I still would like to peek at your sheet if possible

Just look up gamma correction. The ESPHome light platform uses this, with a default gamma of 2.8. All you need to do is scale your brightness to a range of 0–1, raise it to the power of the gamma value, then scale back as you wanted.

The numbers given in the original post #1 for LEDs are close to a gamma of 1.6, and the incandescents close to 2.2 — although both are “boosted” at the low end more than the function suggests.

Try this in the Template editor as an example:

{% set gamma = 1.6 %}
{% for level in range(0, 16) %}
{% set brightness = (((level / 15) ** gamma) * 255) | int %}
{{ level }}: {{ brightness }}
{% endfor %}
1 Like

The reason for the “boost” behavior compared to the perception curve is the need to factor in the non-linearity of the various types of luminaires. For example, due to the high cold resistance and low resistance in normal operation, the incandescent to needs to have a bigger change at the low end to equal the desired luminance change (appx Vratio^3.5). This is even more noticeable for a CFL source because they don’t “strike” until a certain power level is met. LEDs are much more linear so they should follow the perception curve better once they jump past the driver flicker point at the low end.
Of course, all of this is subject to the driver and it’s characteristics. I did not attempt to characterize that because there are so many possible arrangements - PWM, constant current, etc.
The spreadsheet is ripe for improvements. For example, better handling of specific brand characteristics, better error checking and warnings, better documentation, full solution to the mapping curve to avoid iteration, multiple perception curves (tristimulus) to handle RGB as well as White, etc.
I now have the sheet cleaned up enough where someone besides me should be able to use it and will post it to my GitHub soon, depending on my “spare time”. Thanks for the interest!

I have it cleaned enough for it to make sense to someone besides me. I will post it “real soon” depending on spare time.