Skip to content
Hazelsong is still being built.Skip to content
Hazelsong

Publish

Do your testimonials sell? Find out, in purchases

A share of your website's readers is held back and shown no testimonials; everybody else sees your widget as usual. Purchases are counted in both groups, so the difference between them estimates the effect of offering your testimonials to the browsers assigned to see them — measured on your own customers, with a range, not quoted from somebody else's study. Or compare two layouts of the same testimonials, and see which one sells more.

PlanStarter and Pro, out of the account's counted events (2,200 a month on Starter, 8,000 on Pro), which the wall's own views, plays and clicks draw on too, shared by your projects.

How it works

Lift test, step by step

  1. What it measures, exactly

    Every browser the draw puts in the shown group counts, whether or not its reader ever scrolled to the widget, and every browser held back counts the same way. So the difference estimates the effect of offering your testimonials to the browsers assigned to see them (an intention-to-treat estimate), not the effect on the readers who looked at them, and it comes with its range. A browser is the unit: a person who clears their storage or comes back on another device is drawn again.

  2. Inconclusive means inconclusive

    When the range of the difference still crosses zero, the report says the result is inconclusive and how large a difference the test can detect at its size: only a measured difference larger than half the range's width, either way, gives a verdict. A smaller effect, or none, cannot be told apart, and the email at a planned end says the same.

  3. Two groups, drawn at random

    Each reader's browser falls into one group the first time it meets your widget and stays there for the whole test. You choose how many are held back: 10, 25 or 50 percent.

  4. Only after your visitor agrees

    Nothing of the test is stored in, read from or sent from a visitor's browser until your site says they agreed: window.hazelsong.consent(true) from your consent banner, consent=granted on the script tag once they have, or Google Consent Mode's analytics_storage, which the widget reads on its own. A refusal from any of them wins, and consent(false) also removes the entry an earlier yes stored. If your visitors' law asks no consent for this, consent=not_required on the tag says so; that is your statement, and Hazelsong does not check it. Until one of these, the visitor sees your wall exactly as everybody else does and is not in the test.

  5. Who the verdict is about

    Visitors who agreed, from the page where they agreed onward. Visitors who never agreed are not counted at all, so the report cannot say how many were left out, and a site where few agree takes longer to reach a verdict. A visitor who agrees after the wall was drawn joins on their next page; if they then fall in the held-back group, they have seen your testimonials once.

  6. Or compare two layouts

    Both groups see your testimonials: Layout A is the one your embed code already uses, Layout B is the one you pick from the 23 layouts, and readers are split 50/50. Both are counted at the same moment, purchases are counted in each, and the verdict says whether Layout B sells more, sells less, or makes no clear difference yet — with money per reader for both.

  7. You change your embed, not us

    Hazelsong will not reorder your wall on its own. The page says which layout did better and shows the embed code for Layout B beside the result, so switching is one copy — and staying is no copy at all.

  8. The split is checked

    Once 100 readers are counted, the readers in each group are checked against the share you set — 25% held back should come out near 25/75. When the split is off by more than chance allows (a chi-square test, p below 0.001), the page says so above the verdict: the figures cannot be trusted, and the usual causes are a cache serving one version of the page, a second copy of the script tag, or a page that removes the widget.

  9. Purchases come from your own page

    One script tag on the page where an order is confirmed, with the order's amount, or one line of JavaScript where the widget already is. A purchase is counted only from a browser that entered the test, in the group it was drawn into, and each reader is counted as a buyer once. When the purchase reaches us a few milliseconds before that browser's own count — two messages from one page — it brings the count with it, so it is not lost, and the count that follows is not counted again.

  10. Or from your server, with refunds

    When the order is confirmed on your payment provider's own page, the reader's browser hands your checkout a reference — the test, the group and the random number that browser drew for the test, nothing that names anybody — and your server sends it back with the order's id through the API. A reference whose reader the test never counted is refused. Each order counts once however often the provider retries, a reader is a buyer once whichever way the purchase arrives first (a later order of the same reader adds its money and no purchase), and a refund takes it back out, so money per reader is what you kept. If your page and your server both report the same order, it counts once, with your server's amount: the first order your server sends for a reader whose page already reported a purchase is taken to be that purchase. Give the page the order's id — purchased(49, { orderId: "ch_123" }), or &order_id= on the tag — and a different order of the same reader is told apart and adds its money instead.

  11. Or straight from your store

    Connect Stripe, Shopify, WooCommerce or Lemon Squeezy in the dashboard and each paid order that carries the reference is counted in its group with no server of yours — the reference still has to reach the checkout from the reader's browser, which is one line on your page. A refund of the whole order takes the purchase back out; a partial refund leaves it counted.

  12. Or an action, if you sell away from your site

    Choose an action instead of a purchase — a form sent, a call booked — and mark it on your page with one attribute. It is counted once per browser, the same way, and carries no money.

  13. Robots and your own visits are left out

    A browser that says it is automated, or names itself a crawler or a preview fetcher, never enters the test. Open your own site once with ?hazelsong=ignore and your browser is left out too. The same holds whichever way a purchase arrives: your server and your store can only count a purchase for a reference that one of those counted browsers handed out, and the older reference without the browser's random number, which could not be held to that, is refused for every test started from October 18, 2026.

  14. When a purchase counts

    A purchase counts when it arrives after its reader was counted and before the test ends: when you stop it, at the end of the day you chose (UTC), or at the daily check after both groups reach the readers you planned. There is no shorter window per reader: a reader who comes back to buy weeks later, while the test runs, still counts. A refund or a renewal of an order counted in that time is still read after the test ends.

  15. What changed while it ran, beside the verdict

    A verdict is about the wall your readers were offered, and that wall can change while a test runs. Every change made in Hazelsong to what your widget shows — a testimonial published, one taken off the widget, a change to how a published one is shown (its name, a removal shown, featured, labels, the answer added under it, a video), and a change to the widget's color, corners, notice, your project's name or your list of sites — is counted by kind while the test runs, and the report prints the count beside the verdict, saying the verdict is about the wall across those changes. It cannot see what changes on your own pages: your embed's options, the page around it, prices or a sale.

  16. A verdict only when there is one

    Purchase rate, money per reader and the difference between the groups with its range. No verdict is printed before 200 readers in each group and 20 purchases between them, and "helps" means the whole range sits above zero. A comparison of two layouts is held to the same thresholds and the same range, and says "Layout B sells more" only when the whole range sits above zero.

  17. A range you may read every day

    The range is built so that checking it every day does not make a false "helps" more likely. An ordinary 95% interval holds only if it is read once, at a size fixed in advance; read daily, it is wrong far more often than one time in twenty. This one is a confidence sequence by betting (Waudby-Smith and Ramdas, "Estimating means of bounded random variables by betting", Journal of the Royal Statistical Society Series B, 2024): it holds at every number of readers, from the first few hundred on, not only once a test is large, so it is somewhat wider than that interval. It rests on readers being independent and split at the share you set, which the split check watches.

  18. What your plan can detect, before it starts

    Before you start, the panel works out from the same range what the plan's size can find at the purchase rate you type (3% if you type none): the smallest measured difference that could give a verdict, and about how large a real rise must be to give one in 8 tests of 10. At 1,000 readers in each group and 3%, that smallest difference is about 3 points. It also says how many counted events the plan needs at the least and whether your month can carry them. It is arithmetic on your plan and your rate, not a forecast of your traffic, and the same figure is shown while the test runs.

  19. It ends when you said

    When you start a test you choose how long it runs: to the end of a day 7 to 90 days ahead, or until each group has 500, 1,000, 2,500, 5,000, 10,000 or 25,000 readers, or until you stop it. The report shows the days left or the readers so far in each group. When the plan is reached, the test stops the way your own Stop does — every reader sees your wall as your embed draws it, the figures stay — and you get one email with the verdict. Choosing "until I stop it" for a holdout means that share of your readers keeps seeing no testimonials until you do, and the report says so.

  20. By where readers came from, and by page

    Each reader is counted under the site their browser came from — its name only, or "direct" — and the page they entered the test on, with any number, id or email address in its path shown as *. The report gives each origin and each page a row with the same verdict, the same thresholds and the same kind of range as the whole test, so most rows say not enough yet until they have 200 readers in each group. A row's range is corrected for every row a test can have, 423 across the three tables, so that across all of them together the chance of any false verdict stays at 5% or below; a row's range is therefore wider than the whole test's, and a row verdict is rarer. Only purchases your own page reports reach these rows: one sent from your server or your store carries no origin and counts in the totals alone. Up to 200 origins and 200 pages a test; anything past that is counted together as (other).

  21. By product

    Name what was bought — purchased(49, { product: "annual" }), &product=annual on the tag, data-hazelsong-product on a marked button, or product from your server — and each product gets a row: its purchases among all the readers of each group, with the same verdict. Up to 20 products a test.

  22. Renewals, with and without

    A later payment of a subscription bought during the test is sent with the same reference as kind renewal from your server, or straight from Stripe's invoice.paid or Lemon Squeezy's subscription_payment_success. It adds money to its group and never a purchase, so the verdict does not move, and the report shows money per reader both with and without renewals.

  23. Your own currency

    Choose the currency your figures are in, from 20 common ones. It labels the money on the report, on share links and on the result picture, and converts nothing: every amount is kept as it was reported. So it cannot change while a test runs — every amount already counted was reported in the current one — and a change is refused with a sentence that says so; stop the test, or choose it before you start the next one. An order from your store in another currency is counted as a purchase with its amount left out, and the report says how many.

  24. Which testimonials your buyers read

    Every card whose words a reader had on screen for a second is noted, and goes with that reader's purchase. Each testimonial gets its purchase rate among the readers who read it, with a range, once 100 readers have read it — ranked by the low end of the range, so a card read by three people cannot top the list on luck.

  25. The result as a picture

    Once there is a verdict, the report offers the result as a 1200×630 picture to post or send: your project's name, the dates, each group's readers and purchases (or actions) and rate, the difference with its range, money per reader in a purchase test, and the verdict in the same words the report uses. Hazelsong's name is on it only if you check the box that says so; it starts unchecked, because the paid plans carry no Hazelsong credit. A client you gave a share link can download the same picture from it, without our name, for as long as that link works.

  26. It pauses itself

    Readers and purchases are counted against a monthly allowance of events: a reader, a purchase or an action is one, one more when it is also counted by where it came from and its page, one more for a product, and each card a buyer read is one. When it is spent, every reader is shown your testimonials again, in your embed's own layout, and counting resumes when the month turns; when only the rows cannot be paid for, the reader or purchase still counts and the rows leave it out. Nobody is left without your wall because a counter ran out.

Safe by design

The rule that makes it safe to have

Clear boundaries

What it will not do

Every capability page ends with this list. It is the part that says what you are actually buying.

  • It does not predict or project revenue, for the whole test or for any origin, page or product. Every figure is counted, and a row's money per reader is what was reported divided by the readers counted.
  • Its rows by origin and by page do not count purchases sent from your server or your store. The reference those carry is the test, the group and the browser's random number for the test, and it is not extended with where the reader came from, so browsing data never lands in your store's records.
  • It does not count a purchase from a browser that never entered the test, or count the same reader as a buyer twice. It does not count the money of one order twice when your page and your server or store both report it: your server's amount is kept. Without the order's id on the page, the first order your server reports for a reader after the page's purchase is taken to be that purchase.
  • The currency setting does not convert anything. It labels the amounts as they were reported, it cannot change while a test runs, and an order in another currency adds no money.
  • It does not count a purchase after the test ends — after your Stop, the end of the day you chose, or the daily check that finds the readers you planned — and it has no shorter window per reader.
  • Its count of changes during a test sees only changes made in Hazelsong, not changes on your own pages, and it does not stop you from making them.
  • It never stops a test early by itself because one group is ahead. It stops only on the plan you chose when you started it — a date, or a number of readers in each group — or when you stop it.
  • It does not say which testimonial made somebody buy. The card list shows what buyers read; the held-back group is what estimates an effect, and only for offering the wall as a whole.
  • It does not hold back your hosted wall or your collection pages — only the widget on your own site.
  • It does not follow a reader across sites or build a profile of anybody.
  • It does not show a consent banner of its own or decide whether your visitors' law asks for one. It waits for your site's answer, and without one nobody is in the test.
  • It does not count visitors who did not agree, not even as a number left out, so it cannot tell you how many there were.
  • It does not decide anything for you: the figures can be posted by anyone who finds the address, so nothing is charged or changed because of them.
  • It does not credit the cards a buyer read when the purchase comes from your server or your store: the reference carries the group, not the reading.
  • It does not count an order from your store that does not carry the reference: without it there is no group to count it in.
  • It does not switch your layout when one wins. It will not reorder your wall on its own; you change your embed code yourself.
  • In a comparison of two layouts, it does not rank cards for Layout B. The list of cards buyers read counts Layout A only, because one layout keeps a card on screen longer than another and mixing them would rank cards partly on the layout.
  • It does not give a verdict it cannot stand behind: when the readers did not split at the share you set, it says the figures cannot be trusted rather than printing them as a finding.
  • The result picture will not show a projection — no yearly figure, no "you would have made", no relative uplift standing in for money — and it is not offered at all before there is a verdict.
  • The result picture does not carry Hazelsong's name unless you check the box, and it has no public address: it comes from your report, or from a share link you made, under that link's own password and expiry.

Where it lives in the code: lib/lift.ts · lib/lift-claims.ts · lib/lift-drift.ts · lib/lift-drift-rules.ts · lib/lift-window.ts · lib/lift-currency-lock.ts · lib/lift-stats.ts · lib/lift-dims.ts · lib/currency.ts · lib/lift-plan.ts · lib/lift-schedule.ts · app/api/cron/sync/route.ts · lib/lift-card.tsx · app/api/lift/card/route.tsx · app/share/[id]/card/route.tsx · lib/widget-templates.ts · lib/layouts.ts · lib/bots.ts · app/api/lift/route.ts · app/api/v1/lift/purchases/route.ts · app/api/v1/lift/refunds/route.ts · app/api/in/[id]/route.ts · app/widget/[slug]/route.ts · components/LiftPanel.tsx · scripts/lift.mjs · scripts/lift-dimensions.mjs · scripts/lift-nonce-check.mjs

This is running now

Open a project and use it. Nothing on this page is a roadmap.