Clarity before change.

Independent website review

MyBishBash Photobooth

Website review

A full source-code and hands-on review of what MyBishBash Photobooth — our own product — ships, says and asks visitors to do.

Key judgement · At risk

The engineering here deserves a commercial layer that works: the rendering engine, the consent architecture and the pricing substrate are better than most funded products at this stage. What stands between them and revenue is three disconnected exits — fix those before anything else, including before the planned domain migration, which changes the address but fixes none of them.

https://raes-photo-booth.vercel.app/

Inspected 9 August 2026

White-box review · full source access (our own product)

What we noticed

A clearer view of what the website is doing now.

A GreatInternet review of what MyBishBash Photobooth says, shows and asks visitors to do—based on full source access and hands-on sessions: it is our own product, reviewed to the standard we sell.

Illustrative sampleFounder Strategy Review viewSave this view as PDF

What we looked at

White-box audit: full source review (38 files, ~17,986 lines) plus hands-on sessions in the live product at desktop and mobile viewports as a first-time anonymous visitor, completed 9 August 2026. This pack translates that audit into our standard report contract; it is dated and presented as an assessment as of 9 August 2026, not a re-audit of the product as it stands today. Sample pack authored under our own methodology, the same one we sell: it is our own product, reviewed to the same standard as a customer's.

The short version

MyBishBash Photobooth is a genuinely good product with no working way to pay for it, no working way to contact anyone about it, and no way for a free user to be told why the thing they just spent five steps configuring did nothing. The rendering engine, the consent architecture and product.js are better than most funded products at this stage. The commercial layer is the problem, and it is not one bug: every paid tier dead-ends, every Business contact route 404s, and a completed free setup saves nothing.

The engineering here deserves a commercial layer that works: the rendering engine, the consent architecture and the pricing substrate are better than most funded products at this stage. What stands between them and revenue is three disconnected exits — fix those before anything else, including before the planned domain migration, which changes the address but fixes none of them.

The first useful move

Reconnect the contact mailto:, decide the checkout state, and stop the silent save from discarding a user's work — in that order, roughly a day of combined effort.

Overall signal

A directional view, not a grade.

at risk

Genuinely excellent engineering craft (imaging, consent architecture, and product.js's price/capability separation all score 9/10 in the source audit) sits on top of a commercial layer that currently converts zero: every paid tier, every Business contact route and the free-to-paid save action are all disconnected, and the site sells to UK consumers with no terms or privacy policy.

What would strengthen it: Reconnect the three commercial exits (contact, checkout state, silent save) and publish terms/privacy/refund — both are scoped as near-zero-risk, independent of the planned domain migration, and mostly already specified in the source repository's own implementation tracker.

Significant evidence indicates this area may undermine customer confidence, outcomes, or business performance.

The useful diagnosis

Biggest opportunity and biggest risk

Biggest opportunity

[F-01, source severity Blocker] Every paid tier says the shop is shut

high confidence

Clicking any of the three paid plans renders "This service is not available yet." with class checkout-status error. curl to /v1/plans returns 404, identical for all three plans.

Directly reproduced live and confirmed by a same-day curl check of the billing endpoint.

Biggest risk

[F-02, source severity Blocker] The Business funnel has no contact route at all

high confidence

All four "TALK TO US" CTAs resolve to https://mybishbash.app/contact, which returns 404. Site-wide there are zero mailto: and zero tel: links.

Directly confirmed by curl across all four CTA locations; the domain itself is live, isolating the fault to this one path.

Additional legacy finding

[F-03, source severity Blocker] Customise My Booth silently discards a free user's work

high confidence

Completing all five steps of the Customise My Booth flow and clicking Save produces no visible message and writes nothing to localStorage.

Directly reproduced live with a concrete before/after localStorage check.

Additional legacy finding

[F-04, source severity High] One Safari-flavoured alert for six different camera failures

medium confidence

Clicking START PHOTOBOOTH with the camera unavailable fires a native dialog reading "Please allow camera access in Safari and try again." — regardless of the actual cause.

The failure and its cause are directly evidenced in source; the in-app-browser behaviour itself is explicitly [verify by hand] in the audit, since the audit browser cannot reproduce in-app browser restrictions.

One immediate recommendation

  1. 01

    Reconnect the three disconnected commercial exits — contact, checkout, and the silent save — before any other work.

  2. 02

    Publish terms, privacy and a refund notice before the first live transaction.

  3. 03

    Reprice Personal around the event, not the subscription duration, and show the real free-vs-paid difference on the pricing card.

Full website audit

Everything we found, with the evidence behind it.

DimensionBandWhat we sawWhat would raise it
PositioningsoundThe hero, honest live-rendered demos and clear structure are strong marketing craft (7/10 in the source audit); undermined by a mobile-nav defect that lands directly on the hero and a paywall that leads with its weaker lever.Fix the mobile-nav overlap (F-21) and lead the pricing card with the real event-name differentiator (F-23), both already specified and low-effort.
Journey & conversionat riskEvery commercial exit is disconnected: all three paid tiers dead-end, all four Business contact CTAs 404, and a free user's completed setup work is silently discarded on save. The source audit scores commercial completeness 0/10.Reconnect the three exits in the order the audit recommends: the contact mailto: first, the checkout decision second, the silent save third — collectively close to a day of work.
User experienceneeds attentionThe rendering engine and core booth experience are excellent (9/10 imaging craft in the source audit); every failure mode in the product is silent rather than explained, from the discarded save to the one-size-fits-all camera alert.Ship the two forgiveness fixes already specified: name the reason at every point of denial (F-03, F-04, F-12), branch the camera alert on the real error.
Trustneeds attentionThe consent architecture is genuinely excellent and correctly built (9/10 in the source audit) — but the site sells £30-£100 to UK consumers with no terms, privacy policy or refund notice anywhere, and the entitlement enforcement is client-side.Publish terms, privacy and a refund/cancellation statement (already drafted, per the source repository's tracker, pending only a legal classification decision) and decide the entitlement-enforcement posture explicitly rather than by accident.
Content & clarityneeds attentionPersonal pricing is framed in subscription months for a one-evening event, and the pricing card leads with the weaker of its two real differentiators — both are copy/framing fixes, not rebuilds.Reprice around the event, not the duration (F-22), and put the two covers side by side on the pricing card so the real differentiator is shown, not described (F-23).
Mobileneeds attentionA 2.23 MB image is 96% of the page weight on the connection profile most guests actually have, the mobile nav overlaps the hero, and 22 demo controls fall under Apple's touch-target guidance on the surface that does the selling on mobile.Crop and re-encode the demo image (F-17, expected ~2.23 MB -> under 200 KB), fix the nav containing-block bug (F-21), and raise the demo chips to 44px (F-25) — all independently shippable in hours to days.
at risk
Significant evidence indicates this area may undermine customer confidence, outcomes, or business performance.
needs attention
Meaningful gaps create friction or uncertainty and should be addressed deliberately.
sound
The foundations work well, with limited gaps that do not materially obstruct the customer journey.
Findings in severity order.
#SeverityFindingEffort
01high[F-01, source severity Blocker] Every paid tier says the shop is shutmedium
02high[F-02, source severity Blocker] The Business funnel has no contact route at alllow
03high[F-03, source severity Blocker] Customise My Booth silently discards a free user's workmedium
04high[F-04, source severity High] One Safari-flavoured alert for six different camera failureslow
05high[F-05, source severity High] A shareable product with nothing to sharelow
06medium[F-06, source severity Medium] /business is metadata-identical to /medium
07medium[F-07, source severity Medium] No robots.txt, no sitemap.xmllow
08medium[F-08, source severity Medium] Unbranded, unnavigable 404medium
09high[F-09, source severity High] The domain move strands every existing userhigh
10medium[F-10, source severity Medium] Legacy grandfathering will silently expire at the new originmedium
11low[F-11, source severity Low] Founding scarcity is asserted, never evidencedlow
12high[F-12, source severity High] No upgrade route exists at any point of denialmedium
13high[F-13, source severity Blocker] Selling to UK consumers with no terms, privacy policy or refund noticemedium
14medium[F-14, source severity Medium] Privacy claims are made in marketing but nowhere bindinglow
15medium[F-15, source severity Medium] A localStorage key unlocks Personalhigh
16medium[F-16, source severity Medium] Entitlement is a browser variablehigh
17high[F-17, source severity High] 2.23 MB of PNG is 96% of the pagelow
18low[F-18, source severity Low] Three <h1> elements in one documentlow
19high[F-19, source severity High] /photobooth without a trailing slash breaks every assethigh
20medium[F-20, source severity Medium] /business needs a rewrite the parent domain does not yet havemedium
21high[F-21, source severity High] Mobile nav detaches from the header and lands on the herolow
22high[F-22, source severity High] Personal sells six months of a one-evening eventlow
23high[F-23, source severity High] The paywall undersells itselflow
24medium[F-24, source severity Medium] Kicker labels fail AA contrastlow
25medium[F-25, source severity Medium] 22 sub-44px touch targets on the interactive demomedium
26low[F-26, source severity Low] No skip linklow

Strengths to preserve

  • product.js is the quality benchmark in the repository: 802 lines, zero dependencies, deep-frozen throughout, with prices and capabilities structurally separated so a price edit cannot widen access.
  • The consent architecture correctly separates marketing and publicity permission, requires the exact wording and its version in every consent record, and never infers consent from an email address.
  • The rendering engine is a documented pixel pass — including a written record of a Safari-17 filter regression and its fix, which is exactly the institutional memory that stops it being "simplified" back later.
  • The landing page demonstrates the product with the product: the same renderers the booth uses, driven from real input photographs, not mockups.
  • Both test suites are green and meaningfully scoped (17/17 browser, 14/14 Worker), covering consent independence, Stripe signature verification and payment lifecycle.
  • Accessibility fundamentals most sites miss are present: a correct :focus-visible outline on real Tab presses, prefers-reduced-motion honoured, and AA contrast passing on 7 of 8 sampled text roles.

What to leave alone

  • product.js's design — the separation of PLAN_METADATA from CAPABILITY_MATRIX and the CHECKOUT_POLICY comment stating a price change must never grant a feature.
  • The consent-record architecture (validateConsentSubmission, buildConsentRecord).
  • The rendering engine (covers.js, polaroid.js, mp4.js, fonts.js) and its documented Safari-17 workaround.
  • The service worker's explicit refusal to cache anything carrying an Authorization header.

What we noticed · 1

high confidence

[F-01, source severity Blocker] Every paid tier says the shop is shut

Severityhigh
Effortmedium
Confidencehigh

Conversion · Commercial · Commercial

Clicking any of the three paid plans renders "This service is not available yet." with class checkout-status error. curl to /v1/plans returns 404, identical for all three plans.

What the evidence shows

Live reproduction on the production site plus a curl check of the billing API.

Evidence

app.js:1534 (startCheckout); index.html:12 (empty photobooth-api-base meta); worker/ (written, 14/14 tests passing, not deployed)

Likely root cause

Appears to be: startCheckout posts to /v1/billing/checkout, but API_BASE resolves to "" because the photobooth-api-base meta tag is empty, so the request goes same-origin to Vercel, which serves static files only. The Worker exists and is tested but is not deployed.

Based on publicly available evidence; not verified.

Where it happens

100% of prospective paying customers, at the moment of trying to pay

Why it matters

The site advertises three prices and can sell none of them; every purchase attempt ends in an unresolvable error.

Worth doing next

Either deploy the Worker and proxy /v1, or — if that is not imminent — replace the price CTAs with an email-capture waitlist. Advertising a price you cannot take is worse than advertising no price.

high confidence

Directly reproduced live and confirmed by a same-day curl check of the billing endpoint.

What we noticed · 2

high confidence

[F-02, source severity Blocker] The Business funnel has no contact route at all

Severityhigh
Effortlow
Confidencehigh

Conversion · Commercial · Commercial

All four "TALK TO US" CTAs resolve to https://mybishbash.app/contact, which returns 404. Site-wide there are zero mailto: and zero tel: links.

What the evidence shows

curl -L against the contact URL from all four CTA locations; a full link inventory of both surfaces.

Evidence

index.html:11 (business-contact-url meta pointing at a page that has never existed)

Likely root cause

Appears to be: The contact URL was set in metadata before the page it points to was ever built.

Based on publicly available evidence; not verified.

Where it happens

Every Business prospect, the highest-value tier

Why it matters

The entire Business sales funnel terminates in a 404; not one enquiry can currently reach the business.

Worth doing next

Point at a mailto: today — it costs one line and cannot 404. Build the contact page before the Business surface gets any traffic.

high confidence

Directly confirmed by curl across all four CTA locations; the domain itself is live, isolating the fault to this one path.

What we noticed · 3

high confidence

[F-03, source severity Blocker] Customise My Booth silently discards a free user's work

Severityhigh
Effortmedium
Confidencehigh

Conversion · Trust · Core journey

Completing all five steps of the Customise My Booth flow and clicking Save produces no visible message and writes nothing to localStorage.

What the evidence shows

Live reproduction: typed a real event title, saved, and confirmed Object.keys(localStorage) returned [].

Evidence

app.js:1494 (savePersonalSettings: bare early return with no user-facing consequence); app.js:1690-1691 (openPersonalSettings has no entitlement check at the entrance)

Likely root cause

Appears to be: savePersonalSettings opens with a bare early return when the user lacks entitlement, while the entry points that open the setup flow carry no entitlement check at all — so free users are invited into a flow whose exit is silently disabled.

Based on publicly available evidence; not verified.

Where it happens

Every free user who engages with the Personal upsell — the most interested segment

Why it matters

The single worst bug in the product also destroys the work of its most engaged, closest-to-paying users, with no explanation.

Worth doing next

Move the gate to the save action itself: let free users configure everything, then convert Save into the paywall moment — they have already done the work and seen their own event name, the strongest possible moment to ask. That turns the worst bug into the best conversion point on the site.

high confidence

Directly reproduced live with a concrete before/after localStorage check.

What we noticed · 4

medium confidence

[F-04, source severity High] One Safari-flavoured alert for six different camera failures

Severityhigh
Effortlow
Confidencemedium
VerificationReproduced

Core journey · Accessibility · Core journey

Clicking START PHOTOBOOTH with the camera unavailable fires a native dialog reading "Please allow camera access in Safari and try again." — regardless of the actual cause.

What the evidence shows

Live reproduction of the camera-denied path.

Evidence

app.js:931 (single catch wraps the whole capture routine without inspecting err.name)

Likely root cause

Appears to be: A single catch handles NotAllowedError, NotFoundError, NotReadableError, an insecure context, a video.play() rejection and a gallery-write failure identically, with Safari-specific wording regardless of browser.

Based on publicly available evidence; not verified.

Where it happens

Every user whose camera fails to start, concentrated in in-app browsers

Why it matters

The product's real distribution channel is a link pasted into a WhatsApp or Instagram group; those open in in-app browsers that restrict getUserMedia and are not Safari — the most likely real failure mode is a guest being told to fix Safari inside Instagram.

Worth doing next

Branch on err.name, replace the native alert with in-page state, and add an explicit "open in Safari/Chrome" route for in-app browsers.

medium confidence

The failure and its cause are directly evidenced in source; the in-app-browser behaviour itself is explicitly [verify by hand] in the audit, since the audit browser cannot reproduce in-app browser restrictions.

Verification · Reproduced

The behaviour was observed again on re-check.

How it was checked
Source review of the single catch handler plus a live reproduction of the camera-denied path in the audit browser.
What was observed
The same Safari-specific alert fires for every distinct camera failure cause; in-app-browser behaviour specifically could not be reproduced by the audit tooling.
Environment
Audit browser session. The source audit calls for opening the live link inside WhatsApp and Instagram on a real iPhone and a real Android handset before treating the copy fix as verified.
Last checked
9 August 2026

What we noticed · 5

high confidence

[F-05, source severity High] A shareable product with nothing to share

Severityhigh
Effortlow
Confidencehigh

Conversion · Discoverability · Discoverability

og:* tag count: 0. twitter:* tag count: 0. No canonical link. Zero JSON-LD blocks, on either surface.

What the evidence shows

Enumerated every <meta> tag on both the Personal and Business surfaces.

Evidence

index.html:3-17 (hand-authored <head> with no social metadata added)

Likely root cause

Appears to be: Social metadata was never added when the head was authored.

Based on publicly available evidence; not verified.

Where it happens

Everyone who receives a shared link — the primary acquisition path for a party product

Why it matters

A link pasted into the exact channel this product is distributed through (a group chat) renders as a bare, unbranded URL rather than the rich keepsake preview the product could show.

Worth doing next

Add a full og:/twitter: set with a rendered keepsake as og:image — the product already generates the exact image that should sell it.

high confidence

A complete, direct enumeration of every meta tag on both surfaces.

What we noticed · 6

high confidence

[F-06, source severity Medium] /business is metadata-identical to /

Severitymedium
Effortmedium
Confidencehigh

Discoverability · Discoverability

document.title and the meta description on /business are byte-identical to the homepage, and neither surface has a canonical link.

What the evidence shows

Direct inspection of both routes' document head.

Evidence

vercel.json (both routes serve the same static index.html; the client selects a surface at runtime and nothing updates the head)

Likely root cause

Appears to be: Both routes serve one static document and pick a surface client-side, with no per-surface head update.

Based on publicly available evidence; not verified.

Where it happens

Search engines and link-preview crawlers indexing either surface

Why it matters

Search results and shared links cannot distinguish the Business surface from the Personal one.

Worth doing next

Set title/description/canonical/og: per surface on route selection.

high confidence

Directly evidenced by inspecting both routes' rendered head content.

What we noticed · 7

high confidence

[F-07, source severity Medium] No robots.txt, no sitemap.xml

Severitymedium
Effortlow
Confidencehigh

Discoverability · Discoverability

Both /robots.txt and /sitemap.xml return 404, text/plain, 79 bytes.

What the evidence shows

Direct request to both paths.

Evidence

No robots.txt or sitemap.xml present on the deployment

Likely root cause

Appears to be: Neither file was ever added.

Based on publicly available evidence; not verified.

Where it happens

Search engine crawlers

Why it matters

Search engines have no explicit crawl guidance or sitemap for the site.

Worth doing next

Add both at the final domain, not before — they should name mybishbash.app/photobooth.

high confidence

Directly confirmed by request.

What we noticed · 8

high confidence

[F-08, source severity Medium] Unbranded, unnavigable 404

Severitymedium
Effortmedium
Confidencehigh

Core journey · Core journey

A mistyped path returns a bare 404, content-type text/plain, 79 bytes — no branding, no link home.

What the evidence shows

Direct request to a nonexistent path.

Evidence

vercel.json declares three explicit rewrites and no catch-all, so unmatched paths fall through to the platform default

Likely root cause

Appears to be: No catch-all route or branded error page is configured.

Based on publicly available evidence; not verified.

Where it happens

Any visitor who mistypes or follows a stale link

Why it matters

A lost visitor gets no path back into the product, and this gets more likely once the site moves under a subpath.

Worth doing next

Ship a branded 404 that routes back to the booth.

high confidence

Directly confirmed by request.

What we noticed · 9

high confidence

[F-09, source severity High] The domain move strands every existing user

Severityhigh
Efforthigh
Confidencehigh

Architecture · Trust · Architecture

All persistence (settings, gallery, edition counter, access token) is scoped to localStorage/IndexedDB on the current origin. Moving to mybishbash.app/photobooth is an origin change, and browsers scope both storages by origin.

What the evidence shows

Source review of every persisted key and its storage mechanism.

Evidence

app.js:95-100 (SETTINGS_KEY, ACCESS_KEY, GALLERY_DB, EDITION_KEY, all origin-scoped)

Likely root cause

Appears to be: The existing migration path is a same-origin key rename with no cross-origin equivalent; one is not possible without an explicit export/import or a server round-trip.

Based on publicly available evidence; not verified.

Where it happens

Every current organiser and returning guest, at the moment of the domain migration

Why it matters

Paid access is recoverable via the existing email restore flow, so money is not lost — but settings, gallery and the edition counter have no recovery path and will simply be gone.

Worth doing next

Ship export/import of settings before the cutover, keep the old origin serving a redirect plus a one-time "move your booth" hand-off, and decide explicitly whether the gallery is worth carrying.

high confidence

Directly evidenced by a complete inventory of every persisted key and its storage scope.

What we noticed · 10

high confidence

[F-10, source severity Medium] Legacy grandfathering will silently expire at the new origin

Severitymedium
Effortmedium
Confidencehigh

Architecture · Architecture

legacyProfileAvailable is derived from a localStorage key that will not exist on mybishbash.app.

What the evidence shows

Source review of the grandfathering flag's origin.

Evidence

app.js:161 (legacyProfileAvailable derived from localStorage.raePhotoBoothLiveSettings)

Likely root cause

Appears to be: The same origin-scoping issue as F-09, applied to the grandfathering signal specifically.

Based on publicly available evidence; not verified.

Where it happens

Existing legacy free users who currently have Personal unlocked

Why it matters

Whether legacy users keep their unlocked status after the move will be decided by accident rather than on purpose, unless it is made an explicit decision.

Worth doing next

Decide deliberately: either carry grandfathering across in the hand-off, or end it and say so.

high confidence

Directly evidenced; the same origin-scoping mechanism as F-09.

What we noticed · 11

high confidence

[F-11, source severity Low] Founding scarcity is asserted, never evidenced

Severitylow
Effortlow
Confidencehigh

Trust · Commercial · Commercial

The page claims "Limited to the first 500 Founding Lifetime customers", but the endpoint that would populate the live remaining count 404s, so the claim never loads real data.

What the evidence shows

Source review of the founding-availability loader and its endpoint.

Evidence

loadFoundingAvailability() calls /v1/billing/founding, which 404s (same undeployed-Worker root cause as F-01)

Likely root cause

Appears to be: The undeployed Worker (same as F-01); the static fallback is a reasonable degradation of an otherwise verifiable claim.

Based on publicly available evidence; not verified.

Where it happens

Any visitor who reads the founding-scarcity claim

Why it matters

An unevidenced scarcity claim is weaker than none — especially one that is verifiable by design once the Worker is live.

Worth doing next

Once live, show the real remaining number; until then, remove the unevidenced scarcity line.

high confidence

Directly evidenced; the remaining-count mechanism is explicitly designed to be verifiable once deployed.

What we noticed · 12

high confidence

[F-12, source severity High] No upgrade route exists at any point of denial

Severityhigh
Effortmedium
Confidencehigh

Conversion · Commercial · Commercial

The only paths to pricing are the plan-choice button and one post-checkout scroll; nothing in the settings screen, the booth, or the free branding notice links to pricing.

What the evidence shows

Source review of every link and CTA that could plausibly lead to pricing.

Evidence

app.js:1581, app.js:1689 (the only two paths to pricing in the entire product)

Likely root cause

Appears to be: Denial is implemented as silent absence rather than as a named offer, at every point a free user hits a locked capability.

Based on publicly available evidence; not verified.

Where it happens

Any free user who hits a locked capability anywhere except the two named entry points

Why it matters

The product misses the moment a user has just discovered a reason to pay, repeatedly.

Worth doing next

Every locked capability should name its plan and link to it.

high confidence

Directly evidenced by a complete inventory of pricing-linking CTAs.

What we noticed · 13

high confidence

[F-13, source severity Blocker] Selling to UK consumers with no terms, privacy policy or refund notice

Severityhigh
Effortmedium
Confidencehigh

Trust · Compliance · Compliance

/privacy and /terms both 404, and neither is linked from either footer, while the site advertises £30-£100 purchases and the Business surface describes collecting email addresses and consent records.

What the evidence shows

Direct request to both legal-page paths; full footer link inventory on both surfaces.

Evidence

No terms, privacy or refund/cancellation notice present anywhere on the site

Likely root cause

Appears to be: The compliance layer was never built — not a considered trade-off.

Based on publicly available evidence; not verified.

Where it happens

Every prospective UK purchaser, and every Business prospect whose data the site would collect

Why it matters

UK Consumer Contracts Regulations require pre-contract information and cancellation rights for distance sales, and a privacy policy is required once personal data is processed; Stripe's own account review also expects reachable terms and privacy URLs — this can block the payment integration F-01 is waiting on.

Worth doing next

Publish terms, a privacy policy and a cancellation/refund statement, linked from both footers, before the first live transaction. The product has a genuinely strong position to write from: photos never leave the device on Free and Personal.

high confidence

Directly confirmed by request against both legal-page paths and a complete footer link inventory.

What we noticed · 14

high confidence

[F-14, source severity Medium] Privacy claims are made in marketing but nowhere binding

Severitymedium
Effortlow
Confidencehigh

Trust · Compliance · Compliance

The landing page states "Photos stay on your device. There is no backend photo upload." — true and well-evidenced in code — but it exists only as marketing copy, with no policy document behind it.

What the evidence shows

Source review confirming the claim is accurate, alongside confirmation no privacy policy exists to formalise it (see F-13).

Evidence

Landing-page copy asserting local-only photo storage, with no corresponding privacy policy document

Likely root cause

Appears to be: The compliance layer (F-13) does not exist yet, so a true, well-evidenced technical claim has nowhere binding to live.

Based on publicly available evidence; not verified.

Where it happens

Any visitor evaluating the privacy claim before trusting the product with photos

Why it matters

The product's best trust asset is currently its least durable — a claim rather than a commitment.

Worth doing next

Promote the claim into the privacy policy, where it becomes a commitment rather than marketing copy.

high confidence

The underlying technical claim was directly verified in source; the absence of a binding document is confirmed by F-13.

What we noticed · 15

high confidence

[F-15, source severity Medium] A localStorage key unlocks Personal

Severitymedium
Efforthigh
Confidencehigh

Commercial · Commercial

The Personal gate is client-side and OR'd with a legacyProfileAvailable flag read from a localStorage key any visitor can write from the console; confirmed writable on the live origin.

What the evidence shows

Source review of the gate condition plus a live confirmation the key is writable.

Evidence

app.js:435, app.js:1495, app.js:1503 (gate condition); app.js:161 (legacyProfileAvailable read from a client-writable key)

Likely root cause

Appears to be: A legitimate grandfathering decision (recognise genuine legacy users) was implemented with a client-writable signal that trusts any browser claiming to be one.

Based on publicly available evidence; not verified.

Where it happens

Any visitor who opens the browser console on the live product

Why it matters

For a £30 consumer product this is a low-value target and not uniquely weak (all Personal enforcement is client-side, see F-16) — it is listed because the grandfathering intent and its mechanism have drifted apart into an undocumented honour system.

Worth doing next

Grandfather by issuing real entitlements to known legacy users, or accept the bypass explicitly as a documented decision.

high confidence

Directly confirmed live: the key was writable and the gate condition read it as designed.

What we noticed · 16

high confidence

[F-16, source severity Medium] Entitlement is a browser variable

Severitymedium
Efforthigh
Confidencehigh

Architecture · Commercial · Architecture

Entitlement is module state hydrated from a localStorage value and refreshed against a restore endpoint; branding policy is selected from it at render time.

What the evidence shows

Source review of the entitlement hydration and branding-selection path.

Evidence

app.js:162 (entitlement as module state); app.js:407 (branding policy selected from it at render time)

Likely root cause

Appears to be: Inherent to a local-first static app, and largely the right trade for a £30 party tool — the honest framing is that export attribution is a courtesy, not a control.

Based on publicly available evidence; not verified.

Where it happens

Any determined user attempting to bypass branding or entitlement enforcement

Why it matters

The product pays some architectural cost for an enforcement pretence it cannot actually deliver client-side.

Worth doing next

Accept the trade explicitly and stop paying architectural cost for the pretence — or move export branding server-side for Business only, where white-labelling is worth money. Do not build DRM for a £30 party tool.

high confidence

Directly evidenced by source review of the entitlement and branding-selection code paths.

What we noticed · 17

high confidence

[F-17, source severity High] 2.23 MB of PNG is 96% of the page

Severityhigh
Effortlow
Confidencehigh

Performance · Mobile layout · Performance

A single demo image is 2,232,546 bytes of a 2,331,527-byte critical path — every other asset combined is under 100 KB. The same image at JPEG q82 is 335,630 bytes, an 85% reduction, and the service worker pre-caches the PNG, so every install pays the cost.

What the evidence shows

Measured uncached transfer sizes of every critical-path asset, plus a re-encoding test of the same source image.

Evidence

assets/demo-photos.png (2.23 MB); sw.js:3 (pre-cached); marketing.js:10 (crops to 640 of 1024 downloaded rows — 37% of downloaded pixels never displayed)

Likely root cause

Appears to be: The demo image was never optimised or cropped to the region actually used, and is shipped uncompressed relative to what the page displays.

Based on publicly available evidence; not verified.

Where it happens

Every mobile visitor, disproportionately at venues on poor data — exactly where a party photobooth link gets opened

Why it matters

The single largest asset on the page delivers zero pixels beyond what a correctly sized, correctly encoded image would, at roughly 10x the necessary weight.

Worth doing next

Crop to the 640 rows actually used and encode as WebP with a JPEG fallback; expect roughly 2.23 MB to drop under 200 KB with no visible change.

high confidence

Directly measured transfer sizes and a direct re-encoding test of the same source image.

What we noticed · 18

high confidence

[F-18, source severity Low] Three <h1> elements in one document

Severitylow
Effortlow
Confidencehigh

Accessibility · Accessibility

Three separate <h1> elements exist in the DOM at once ("Your party. Your photobooth.", "Want your brand on the booth?", "Your Celebration") — only one visible at a time.

What the evidence shows

Direct DOM inspection.

Evidence

All surfaces are .screen sections within a single document

Likely root cause

Appears to be: All surfaces (Personal landing, Business landing, booth) live as sections of one document rather than distinct documents or dynamically managed headings.

Based on publicly available evidence; not verified.

Where it happens

Screen-reader users and search engines parsing document structure

Why it matters

Weakens the document outline for assistive technology and search indexing.

Worth doing next

Resolve alongside per-surface metadata (F-06).

high confidence

Directly confirmed by DOM inspection.

What we noticed · 19

high confidence

[F-19, source severity High] /photobooth without a trailing slash breaks every asset

Severityhigh
Efforthigh
Confidencehigh

Architecture · Architecture

Every asset reference is relative. At https://mybishbash.app/photobooth (no trailing slash) the document base is /, so styles.css resolves to /styles.css — wrong. At /photobooth/ it resolves correctly.

What the evidence shows

Source review of every relative asset reference and the service worker's asset list.

Evidence

index.html:13-16 (relative href/src); app.js:1755 (register("./sw.js")); all 14 service-worker ASSETS entries

Likely root cause

Appears to be: Relative paths are the right choice for a portable subpath app, but they make the trailing slash load-bearing, and nothing currently enforces it.

Based on publicly available evidence; not verified.

Where it happens

Everyone, from the moment the migration goes live, if the trailing slash is not enforced

Why it matters

The failure mode is a totally unstyled, non-functional page at first launch on the new domain.

Worth doing next

A permanent redirect from /photobooth to /photobooth/ at the parent domain, plus a <base> tag as a belt-and-braces guard, verified before cutover.

high confidence

Directly evidenced by a complete inventory of every relative asset reference.

What we noticed · 20

medium confidence

[F-20, source severity Medium] /business needs a rewrite the parent domain does not yet have

Severitymedium
Effortmedium
Confidencemedium
VerificationReproduced

Architecture · Architecture

Under the subpath, the relative link to /business resolves to /photobooth/business, which the parent domain must rewrite to the same document; there is no catch-all, so any other /photobooth/* path yields the bare 404 of F-08.

What the evidence shows

Source review of the current vercel.json rewrites plus a live navigation check.

Evidence

vercel.json rewrites exactly /, /business, /business/ — no catch-all; index.html:30 (relative href="business")

Likely root cause

Appears to be: The current rewrite list enumerates exact paths rather than a catch-all pattern.

Based on publicly available evidence; not verified.

Where it happens

Anyone navigating to /business or any other path once the site moves under /photobooth

Why it matters

The Business surface would 404 under the new subpath unless the parent domain's rewrite is updated first.

Worth doing next

Rewrite /photobooth/(.*) to the deployment and let the client route, rather than enumerating paths in two places.

medium confidence

The rewrite gap itself is directly evidenced; one hard navigation to /business timed out during the audit while curl returned 200 and client-side routing worked, so the audit flags this as instrument noise pending a manual recheck under the subpath.

Verification · Reproduced

The behaviour was observed again on re-check.

How it was checked
Live navigation attempt to /business (hard navigation) alongside a curl check and a client-side-routing check of the same URL.
What was observed
The hard navigation timed out while curl returned 200 and client-side routing worked correctly — recorded as instrument noise, not a confirmed defect.
Environment
Audit browser session. Retained only as a check to repeat manually after the subpath migration, not as a standalone finding.
Last checked
9 August 2026

What we noticed · 21

high confidence

[F-21, source severity High] Mobile nav detaches from the header and lands on the hero

Severityhigh
Effortlow
Confidencehigh

Mobile layout · Mobile layout

At 375x812, the audience nav renders 71px below the header it belongs to, in a bordered box overlapping the hero kicker text. Confirmed by programmatic rect intersection.

What the evidence shows

Direct viewport measurement at 375x812 with a programmatic rect-intersection check.

Evidence

styles.css:364 (.audience-nav{grid-row:2;grid-column:1/-1;position:absolute;top:72px} — for an absolutely-positioned grid item, the containing block is the grid area, not the grid container's padding box, so top:72px double-counts the header height)

Likely root cause

Appears to be: The absolutely-positioned nav is also a grid item, so its containing block is its grid area rather than the container — a CSS positioning-model mismatch, not a design error.

Based on publicly available evidence; not verified.

Where it happens

Every visitor at 768px or narrower — the majority

Why it matters

A floating, overlapping navigation box sits directly on top of the hero copy for most mobile visitors, on the page whose entire job is to convert a first-time visitor.

Worth doing next

Drop grid-row/grid-column from the absolutely-positioned element, or use top:100% — the intended tab-bar-off-the-header design is correct, only the containing block is wrong.

high confidence

Directly measured and confirmed by programmatic rect intersection, with the exact CSS mechanism identified.

What we noticed · 22

high confidence

[F-22, source severity High] Personal sells six months of a one-evening event

Severityhigh
Effortlow
Confidencehigh

Commercial · Content clarity · Commercial

Personal pricing is duration-based (£30/6 months, £50/12 months, £100 lifetime), but the customer has one party — nobody has six months of birthdays, and pricing in months forces buyers to reason about a duration rather than the event they already scheduled.

What the evidence shows

Source review of PLAN_METADATA and a reasoned analysis of how a party host actually evaluates the purchase.

Evidence

product.js:107-138 (PLAN_METADATA: £30/6mo, £50/12mo, £100 lifetime capped at 500)

Likely root cause

Appears to be: Pricing was structured around subscription duration rather than the single-event use case the product actually serves.

Based on publicly available evidence; not verified.

Where it happens

Any prospective Personal buyer evaluating which tier matches their one event

Why it matters

The £30 tier is dominated on both price and duration by the £50 annual tier and exists mainly to anchor it, while the whole framing invites an unanswerable question ("will I still want this in month five?").

Worth doing next

Sell the event: "One event, £X" matches how the purchase is actually reasoned about, removes the expiry objection, and makes the upsell to Founding Lifetime a straightforward "host more than twice and this pays for itself." Keep Founding Lifetime unchanged.

high confidence

Directly evidenced pricing structure; the audit explicitly notes this is among the cheapest changes in the whole audit since pricing is presentational metadata by design (product.js's own separation of price from capability).

What we noticed · 23

high confidence

[F-23, source severity High] The paywall undersells itself

Severityhigh
Effortlow
Confidencehigh

Commercial · Content clarity · Commercial

The Free pricing card promotes removing branding, but launchFreeBooth resets free users to a default event title of "Your Celebration" — so every free magazine cover is mastheaded "YOUR CELEBRATION" rather than the guest's actual party.

What the evidence shows

Source review of the free-booth default state and the cover-copy generation path.

Evidence

app.js:1501 (launchFreeBooth resets to DEFAULTS); app.js:2 (eventTitle: "Your Celebration")

Likely root cause

Appears to be: The pricing card leads with the weaker lever (removing branding) while the stronger, already-built lever (the guest's real event name on the keepsake) is never shown.

Based on publicly available evidence; not verified.

Where it happens

Any free user evaluating whether to upgrade to Personal

Why it matters

The real free/paid difference — a keepsake that says the guest's actual party name, not "Your Celebration" — is a far stronger reason to pay, and it is hidden.

Worth doing next

Show it, don't describe it: put the two covers side by side on the pricing card, the same comparison already built for the welcome screen, applied to the difference that actually sells.

high confidence

Directly evidenced by source review of both the default event title and the pricing-card copy.

What we noticed · 24

high confidence

[F-24, source severity Medium] Kicker labels fail AA contrast

Severitymedium
Effortlow
Confidencehigh

Accessibility · Accessibility

The product-kicker pink-on-pill combination measures 3.60:1, below the 4.5:1 required for bold text under 18.66px.

What the evidence shows

Direct colour-contrast measurement of the rendered kicker element.

Evidence

.product-kicker: rgb(217,45,127) bold 10px on its pink pill background

Likely root cause

Appears to be: The kicker's pink was chosen without checking contrast against its own pill background at the size actually used.

Based on publicly available evidence; not verified.

Where it happens

Any visitor relying on text contrast to read section labels

Why it matters

A section-labelling element used across the marketing surfaces falls short of AA on every appearance.

Worth doing next

Darken the pink or lighten the pill.

high confidence

Directly measured contrast ratio against the WCAG threshold for the actual rendered size and weight.

What we noticed · 25

high confidence

[F-25, source severity Medium] 22 sub-44px touch targets on the interactive demo

Severitymedium
Effortmedium
Confidencehigh

Accessibility · Mobile layout · Accessibility

22 mobile controls measure under 44x44 CSS px, including all nine demo frame/filter chips at 29px tall.

What the evidence shows

Direct measurement of every interactive control on the mobile landing surface.

Evidence

Demo frame/filter chips: White 42x29, Film 35x29, B&W 39x29 — all pass WCAG 2.2 SC 2.5.8's 24x24 minimum, but fall short of Apple's 44pt guidance

Likely root cause

Appears to be: The demo chips were sized to fit visually rather than against a touch-target guideline.

Based on publicly available evidence; not verified.

Where it happens

Any mobile visitor using the interactive demo, which does the selling on mobile

Why it matters

The chips that demonstrate the product's actual output — the strongest selling moment on mobile — are the hardest controls on the page to hit accurately.

Worth doing next

Raise the chips to 44px.

high confidence

Directly measured every control; the audit is explicit this is a usability finding against Apple's guidance, not a WCAG 2.2 SC 2.5.8 failure (which these controls pass).

What we noticed · 26

high confidence

[F-26, source severity Low] No skip link

Severitylow
Effortlow
Confidencehigh

Accessibility · Accessibility

No skip-to-content link exists on any surface; keyboard users must traverse the full navigation on every surface change.

What the evidence shows

Direct keyboard-navigation inspection.

Evidence

No skip-link element present in the DOM

Likely root cause

Appears to be: A skip link was never added.

Based on publicly available evidence; not verified.

Where it happens

Keyboard users navigating between surfaces

Why it matters

Adds unnecessary keyboard traversal for a real, if small, share of visitors.

Worth doing next

Add a standard skip-to-content link.

high confidence

Directly confirmed by DOM inspection.

Accessibility note: This assessment identifies observed accessibility considerations from the tested evidence. It is not a formal accessibility conformance certification or legal opinion.

Technical note: Technical observations reflect the tested environment and evidence available during this assessment. Behaviour may vary by device, browser, session state or subsequent site changes.

Website Review & Implementation Pack

The plan, packets and tracking materials for the work.

Do first

  • Point all four Business contact CTAs at a mailto: today. — One line of code removes a Blocker-level dead end; the full contact page can follow.
  • Decide the checkout state honestly: either deploy the Worker, or replace the price CTAs with a waitlist. — Advertising a price the site cannot take is worse than advertising no price at all.

Do next

  • Convert the silent Customise My Booth save into the paywall moment. — It turns the single worst bug in the product into its strongest conversion moment — the user has already done the work and seen their own event name.
  • Publish terms, privacy and a refund/cancellation notice, linked from both footers. — Selling £30-£100 to UK consumers with no terms or privacy policy is a compliance blocker that can also block the payment integration itself.

Can wait

  • The mybishbash.app domain migration and its four dependent architecture findings. — The source audit's own explicit recommendation: none of the three disconnected commercial exits is caused by the current address, and migrating first means carrying every one of them onto the new domain and re-testing there.
  1. 01HoursReconnect the Business contact route with a mailto: link.Removes a Blocker-level dead end at near-zero cost.low effort · small useful change
  2. 02Hours to a dayDecide and implement the honest checkout state; fix the silent save.Closes the two remaining Blocker-level commercial dead ends.medium effort · small useful change
  3. 03DaysPublish terms, privacy and refund policy; add social metadata and the free-vs-paid pricing comparison.Removes the compliance blocker and closes the discoverability and pricing-framing gaps.medium effort
  4. 04WeeksExecute the domain migration as one coherent piece of work, and decide the entitlement-enforcement posture.Structural and irreversible-feeling; the source audit explicitly recommends this only after the commercial exits are fixed.high effort

Pre-call strategic review pack

Hypotheses to test with the founder—not conclusions from the website.

This is a pre-launch product with a genuinely excellent core engine and no working way to convert a single visitor into a customer. It is really selling a keepsake worth showing off, but every current path from interest to purchase or enquiry ends in silence.

What is most in the way: The governing constraint is sequencing, not craft: the engine, consent model and demo experience are already strong; the commercial layer was never finished, and the planned domain migration is being considered before the shop is even open.

Reconnect the three commercial exits before anything else, including the migration

Treat the contact mailto:, the checkout-state decision, and the silent-save fix as a single, ordered, near-zero-risk unit of work that lands before the mybishbash.app migration, not alongside or after it.

None of the three failures is caused by the current domain; migrating first means carrying all three onto the new domain and re-testing them there, while the new domain earns its first impressions on a site that still cannot take money or answer an email.

RequiresFounder agreement on the checkout decision (deploy the Worker now, or replace prices with a waitlist) — the one open call in this bet.

Expected outcomeA site that can actually convert a visitor, on either domain, within roughly a day of combined effort.

RiskIf the migration turns out to be more urgent for an external reason (a booked event, a brand deadline), this sequencing needs revisiting — but the source audit notes these three fixes travel with the code and are not invalidated by the move, so the reverse risk (migrating first) is not symmetric.

high confidence — All three underlying findings are directly reproduced and evidenced, and the sequencing argument is the audit's own explicit, single recommendation.

Publish the compliance layer as a trust asset, not paperwork

Write the terms, privacy and refund policy from the product's genuinely strong position — photos never leave the device on Free and Personal — rather than treating this as a delayed formality.

The product already has a strong, true privacy claim in its marketing copy with nothing binding behind it; formalising it turns the best trust asset in the product from a claim into a commitment, while also closing a real UK consumer-protection and Stripe-account-review exposure.

RequiresA decision on the legal classification of the paid event access (digital content, digital service, or licence/access right), which determines the exact cancellation-rights wording.

Expected outcomeA compliant checkout path and a strengthened, formalised privacy position, unblocking the payment integration.

RiskThe classification question is genuinely legal, not commercial, and should not be resolved by picking whichever category is commercially convenient.

high confidence — The compliance gap and the strength of the underlying privacy claim are both directly evidenced.

Sell the event, not the subscription, and show the real differentiator

Reprice Personal around the single event the customer actually has, and replace the pricing card's "removes branding" pitch with a side-by-side comparison of the real keepsake difference (the guest's actual event name vs. "Your Celebration").

Both fixes are already fully specified, low-effort, and reversible — pricing is presentational metadata in a file explicitly designed so a price change grants nothing — and both directly address a scored 0/10 commercial-completeness gap without any redesign.

RequiresFounder agreement to keep Founding Lifetime unchanged while repricing only the entry Personal tier.

Expected outcomeA pricing page that matches how a party host actually reasons about the purchase, and a paywall that leads with its strongest lever.

RiskLow — the audit is explicit that this is among the cheapest, most reversible changes available.

high confidence — Both findings are directly evidenced pricing and copy structure, with the audit's own explicit cost-of-being-wrong assessment.

Now

  • Reconnect the three disconnected commercial exits: contact mailto:, checkout-state decision, silent-save fix.

Next

  • Publish terms, privacy and refund policy; reprice around the event; show the real pricing-card differentiator.
  • Close the discoverability gap (social metadata, robots/sitemap, branded 404) and the measured accessibility gaps.

Not yet

  • The mybishbash.app/photobooth domain migration — The source audit's own explicit recommendation: none of the three commercial exits is caused by the current address, and migrating first means re-testing all three on the new domain while it launches unable to take money or answer an email.
  • Re-engineering client-side entitlement into a server-enforced model — The audit explicitly cautions against building DRM for a £30 party tool; the honest framing (export attribution as a courtesy) is a legitimate resting point, not a gap requiring an immediate rebuild.

What not to change

  • product.js's architecture — the price/capability separation and CHECKOUT_POLICY — Named directly as the standard the rest of the repository should be held to.
  • The consent-record architecture — Correctly built and the product's real Business differentiator; nothing here needs fixing.
  • The rendering engine (covers.js, polaroid.js, mp4.js, fonts.js) and its documented Safari-17 workaround — Genuinely strong, already-tested craft with its own institutional memory written down in the README.

Open questions

  • Does the Worker ship before or after the domain migration? — Evidence that would answer it: A founder decision: if after, the prices need to come off the page in the meantime — leaving them advertising an unfulfillable price is the worst of the available options.
  • How is the paid one-event access classified under UK consumer law — digital content, a digital service, or a licence/access right? — Evidence that would answer it: A legal opinion on classification; it determines only the cancellation-rights wording and the exact consent/acknowledgement text at checkout, nothing else in the compliance layer depends on it.

Included strategy conversation

Want to talk through the choices?

Your access includes one strategy conversation to discuss your report.

Complete founder questionnaire

Methodology & transparency

Methodology, transparency & limitations

This report has been generated with the assistance of artificial intelligence using the evidence available at the time of assessment. GreatInternet applies a defined assessment methodology and presents observations, judgements and recommendations based on that evidence. AI-assisted output should be reviewed by an appropriate human decision-maker before material reliance or implementation.

Limitations

  • The camera flow (three-shot capture, strip/cover/Polaroid rendering, iOS Share fallbacks) was never exercised — the audit browser cannot be granted camera access — and is assessed from source only. No finding here claims the capture engine itself is broken.
  • The Worker (billing, entitlement, Business API) is not deployed; /v1/* returns 404 on the live origin, so that entire layer is audited as-coded, not as-running.
  • Since this audit (9 Aug 2026), the source repository's own implementation tracker (WORK.md, last updated 2026-08-11) records packets landed against several findings here — PB-01 (F-02, contact mailto wired), PB-02 (F-01, dead-checkout messaging made honest — purchases still cannot complete; BILLING_LIVE remains false), PB-03 (F-03, silent save fixed), PB-05 (F-05/F-06, origin constant and social metadata added), PB-06 (F-17, demo image reduced), PB-07 (F-07/F-08, robots/sitemap/branded 404 added), PB-08 (F-21, mobile nav fix), PB-09 (F-04, camera error branching), PB-10 (F-24/F-25/F-26, accessibility gaps closed). This document does not mark any of these findings resolved: doing so would assert a retest of the live product that this pack did not perform (see the file-level comment in photobooth-sample-report.ts for why). Findings F-09, F-10, F-13, F-14, F-15, F-16, F-19, F-20, F-22, F-23 have no corresponding completed packet as of that tracker entry.
  • Two live defects were found by the source repository's own later work (PB-17, PB-29) that this 9 Aug audit did not itself identify: a silent gallery-trim bug past 20 sessions, and a /business/ trailing-slash routing failure. Neither is a finding in this document, since this pack translates only the 9 Aug audit.