How the formula works

Your score starts with the raw votes you receive: No, Somewhat, Yes, Very. Not every vote counts the same, though. Photofeeler's VoterStyles algorithm reweights each vote based on the voter's history, summarized as a single number from 0 to 10 called their lean (0 = habitual flatterer, 10 = harsh critic).

zphoto=jΦ(W0[vj])(A[vj]+B[vj](Lj5))jΦ(W0[vj])z_{\text{photo}} = \frac{\sum_{j} \Phi(W_0[v_j])\cdot\big(A[v_j] + B[v_j](L_j-5)\big)}{\sum_{j} \Phi(W_0[v_j])}
median=10Φ(zphoto)σz=Cϕ(zphoto)nqp=10Φ ⁣(zphoto+Φ1(p)σz)\begin{aligned}\text{median} &= 10\cdot\Phi(z_{\text{photo}}) \\ \sigma_z &= \dfrac{C}{\phi(z_{\text{photo}})\sqrt{n}} \\ q_p &= 10\cdot\Phi\!\big(z_{\text{photo}} + \Phi^{-1}(p)\cdot\sigma_z\big)\end{aligned}

What each piece does:

  • Base value (AA) is what each button contributes to z when cast by a neutral voter (lean = 5).
  • Lean correction (BB) sets how much the voter's lean shifts that base value. A "Very" from a harsh critic moves z noticeably more than the same lean shift applied to a "No".
  • Trust weight (Φ(W0)\Phi(W_0)) is the share of a vote that survives Photofeeler's quality filter. Low-quality and suspiciously generous votes lose most of their weight.
  • The final score (median\text{median}) runs the weighted z-score through the standard normal CDF Φ\Phi and multiplies by 10. Because Φ\Phi is an S-curve (not the bell itself), z=0z = 0 lands exactly at 5.0 and the extremes saturate near 0 and 10.
  • The 90% range (qpq_p) narrows as the vote count nn grows. Its width at any given z also depends on the bell-curve density ϕ(z)\phi(z) and a global dispersion constant CC.

Finding the weights

We fit these constants on 473 Dating-category tests, covering 25,098 votes across Smart, Trustworthy and Attractive. A Nelder-Mead optimizer tuned them to match both the reported median and the five quantiles of the 90% range at the same time. The fitted formula then reproduces Photofeeler's reported medians to within about 1.7% mean absolute percentage error.

What we learned

The fitted numbers already tell a few stories. Three values matter most. The base score is what a single vote of that type would produce on its own if cast by a neutral voter (lean = 5). The kept weight (Φ(W0)\Phi(W_0)) is the fraction of that vote that survives the trust filter. The lean slope (BB) is the per-lean-point shift each button adds to the z-score. A bigger slope means the same click counts very differently depending on the voter's history. Switch traits to see how they compare:

No
Base score 2.7
Kept weight 100%
Lean slope 0.09
Somewhat
Base score 5.3
Kept weight 94%
Lean slope 0.11
Yes
Base score 8.9
Kept weight 67%
Lean slope 0.19
Very
Base score 10.0
Kept weight 33%
Lean slope 0.39
  • "Very" votes argue hard, but get throttled hard. A single "Very" pulls toward a flat 10.0, yet the trust filter keeps only 26–33% of its weight by default. The algorithm assumes anyone clicking "Very" is probably too generous, so its full weight only returns when a habitual harsh critic casts it.
  • "No" and "Somewhat" pass through almost unfiltered. On Attractive and Trustworthy, kept weights sit near 100% (94–98% on "Somewhat", 100% on "No"). Smart is the slight exception, keeping 84% of a "No" and 90% of a "Somewhat".
  • Harsh critics move the score more than flatterers do. Look at the lean slope row above: on every trait, "Very" is roughly four to five times more sensitive to voter history than "No" (0.39 vs 0.09 on Attractive, 0.39 vs 0.08 on Smart, 0.31 vs 0.06 on Trustworthy). A concrete example on Attractive: a three-point lean jump from neutral (5) to harsh critic (8) adds 0.39 × 3 = 1.17 to a "Very"'s z-contribution, but only 0.09 × 3 = 0.28 to a flatterer's "No". That asymmetry is why a "Very" from someone who usually clicks "No" lifts your score noticeably more than a "No" from a habitual "Very" voter drags it back.
  • "Somewhat" barely moves the needle. Its base score sits near 5 on all three traits, so extra "Somewhat" votes mostly pull the weighted mean toward the middle.

Why a high Smart score is easier than a high Attractive one

The three traits don't share the same score distribution. Across the 472 photos we have medians for, the share scoring 9.0 or higher is 18.6% on Smart, 14.2% on Trustworthy and 7.0% on Attractive. Standout Smart scores are almost three times as common as standout Attractive ones.

  • Smart rewards legible signals. A clean background, good grooming and a fitted suit or blazer read as "put-together" within a second, and voters act on that cue fast. The bar is essentially categorical: you either look the part or you don't. Photos that clear it tend to land in the 8–9+ range rather than sit in the middle.
  • Trustworthy has the highest floor but a thinner top. Its mean is the highest of the three (7.10, versus 6.73 on Smart and 6.74 on Attractive), so most photos drift into the 7s. The 9+ tail is thinner because voters reserve high Trust for warmth cues: a relaxed jaw, real eye contact, a natural smile. Outfit changes can't manufacture that.
  • Attractive is the hardest tail to reach: only 7.0% of photos clear 9.0. The score depends on things you can't change on shoot day (bone structure, skin, body composition) plus things you can (lighting, grooming, wardrobe). Progress usually comes from stacking small wins, not flipping one switch.

There's also a visible coupling between traits. Among the 97 photos scoring 8.0 or higher on Attractive, the mean Smart score is 8.79 but the mean Trustworthy score is only 7.94, a gap of about 0.85. Voters cool on Trust once a photo looks physically striking, reading the subject as posed or edgy. That's why, once you optimize for looks, Smart climbs alongside Attractive while Trust lags behind.

Interactive score explorer

Drag the sliders to see how different vote mixes and voter leans land on the 0–10 scale.

Predicted score
6.04 7.63 / 10 8.79
from 20 votes · 90% range
Trait
Vote counts (0–20 per button)
No
1
Somewhat
6
Yes
11
Very
2
Mean voter lean (0 flatterer → 10 critic)

Defaults to the empirical mean lean for each vote type in our 25,098-vote dataset. Switching trait resets these. "Very" voters run harsher on Trustworthy than on Smart, for example.

No
6.1
Somewhat
4.8
Yes
4.5
Very
4.3

What's in a Photofeeler test record

Behind every result page is a JSON object with the photo, your request settings, the full vote breakdown and the notes voters left. Most of it shows up on screen in some form; a few fields (the per-voter leans, the quantiles) only become legible once you know what they mean.

The test request

activatedAt unix timestamp

Seconds since the Unix epoch for the moment the test went live. Multiply by 1000 if you want to pass it to JavaScript's Date.

voteCount number

How many votes the test has collected so far.

requiredVoteCount number

How many votes you ordered. The test stays open until this number is reached, and the scores keep shifting until it is.

isKarmaPowered boolean

true if the test was funded by karma (credits earned by rating other people's photos), false if you paid for credits directly.

The scores

ratingsByTrait object

The headline 0–10 score for each trait. For Dating, that's SMART, TRUSTWORTHY and ATTRACTIVE. Each value is the median of the score distribution Photofeeler infers from your votes; it's the same number shown at the top of the result page.

quantilesByTrait object

Five points along the score distribution per trait, in the order [p5, p25, p50, p75, p95]. The first and last bracket the 90% range you see drawn on the result page; the middle value matches the headline rating. The closer together they sit, the more confident the estimate.

Per-vote breakdown

scoreTalliesByTrait object

How the raw votes split across the four buttons per trait, in the order [No, Somewhat, Yes, Very]. For example, [3, 6, 9, 2] on Attractive means 3 voters picked No, 6 picked Somewhat, 9 picked Yes and 2 picked Very.

scoreLeansByTrait object

For each button, the lean (voter's 0–10 history score) of every voter who clicked it. The four inner arrays line up with [No, Somewhat, Yes, Very], so their lengths add up to voteCount. A higher lean is a stricter rater, and the formula above gives those clicks more weight.

Two "Yes" arrays, each with eight votes, from very different voter pools:

"Yes": [4, 5, 5, 5, 4, 6, 5, 4]

Eight middle-of-the-road voters all saying Yes. The boost is small, since voters this lenient give Yes out regularly.

"Yes": [8, 9, 7, 10, 8, 9, 8, 9]

The same eight Yes votes, but from people who almost never click Yes. Each click carries far more weight, and the score jumps.

Voter notes

noteGroups array of objects

The Quick Notes left by voters, grouped by note text. Each entry has text (the note itself, e.g. "Great smile!"), count (how many voters picked it) and sentiment (1 for positive, -1 for negative). Free-text comments aren't included here.

View raw algorithm coefficients

The fitted constants used above. The dispersion constant CC is 0.38, shared across all three traits.

Trait Button AA (base) BB (lean slope) W0W_0 (trust pre-image)
SMART No -0.769 0.081 1.000
Somewhat -0.106 0.112 1.278
Yes 1.090 0.187 0.114
Very 4.003 0.392 -0.648
TRUSTWORTHY No -0.704 0.063 3.079
Somewhat -0.063 0.130 2.029
Yes 1.129 0.204 0.170
Very 4.001 0.311 -0.585
ATTRACTIVE No -0.600 0.093 3.323
Somewhat 0.083 0.114 1.597
Yes 1.210 0.186 0.434
Very 4.002 0.392 -0.431