Skip to content
Louis Barkan
All work

PSIgnpost Consulting · Single-cell genomics

From bench to signpost

Seth Brodie is a single-cell genomics scientist with twenty years of bench and platform work, twenty peer-reviewed papers, and, until August, no website. He was launching a consultancy, PSIgnpost, and needed a site that could win the work.

I ran design and build from the first scaffold to DNS, with Claude Code as the build partner, and put his own data on the page as the identity.

Role
Design & build lead
Duration
20 hours · Sep 2026
Format
Five live working sessions
Team
Me: direction, design, brand
Claude Code: build partner
Seth Brodie, Ph.D.: client, copy,
the science
Responsibilities
Product directionInformation architecture
Brand & logo (Figma)Copy editing
Build directionLaunch ops · DNS, email

The outcome

Brief to live domain
in 20 hours.

psignpostconsulting.com went live on 8 September 2026 with the domain cut over from GoDaddy, the contact form delivering to his own mailbox, and search engines admitted the same day. The domain, the mailbox and the email delivery are in Seth's name; the code and the hosting move to his accounts next, with him at the keyboard.

20
hours
5
live sessions
3
kinds of buyer
1
page

Live

psignpostconsulting.com

The problem

The brief was two lines: a sharp marketing site with high-end visualization, and a form connected to a real email address. More important, the site had to turn Seth's CV, a record of employment, into evidence of capability.

He had no online presence of his own. The people he needed to reach were specific: VPs of biology and CSOs at biotech startups, founders, platform companies, and the investors who hire scientists for technical due diligence. None of them could find him, and he needed something they could open within weeks, not a brand program.

The solution

One page, structured for whoever is reading it, with Seth's data as the visual identity.

The goal was a site that wins consulting work and costs Seth almost nothing to keep. Nothing on it changes by visitor, so I decided on a static site: fast, durable, and free for Seth to host on Vercel. Only the contact form runs on the server. I measured everything rather than eyeballing it, from contrast ratios to the swing of the UMAP orbit.

The hero. His own UMAP embedding as the identity, not a stock photograph. Click to enlarge.

The engagement

Five live co-creation sessions, with the site changing as we talked.

Before the first call

I set up the stack ahead of the call so the call could be about Seth and not about tooling. Astro, because it is what my own site runs on and I could reason about every line of it, is the decision every later one sat on. I also agreed to hold the repo and the hosting until launch, so nothing had to be explained before it existed.

In the sessions

In the first session we read three competitor sites together and Seth's reactions became the constraints: a studio portrait that looked contrived, navigation too small to find, a "How I Can Help" that ran on for paragraphs. He wanted clean, dense enough to be taken seriously, and hierarchy from layout rather than from more headers. One of those sites was using a photograph of Seth from his own lab at NCI without his knowledge. He let it go, but it made the point: somebody else was already defining him online.

From then on the sessions were screen-shared and live. Seth reacted, I directed, Claude Code built, and the site changed in front of us. Copy went to Seth between sessions and came back in his own words. He had asked for it this way. He wanted to understand how the site worked, not be handed one, so every step was done where he could see it and ask why.

At launch

The domain moved from GoDaddy to Vercel, the contact form was verified end to end against his real mailbox, and search engines were admitted the same day. The domain, the mailbox and the email delivery are in Seth's name. The code and the hosting move to his own accounts next, and I walk him through that setup rather than doing it for him, so he can run the site without me.

The core

Three decisions carried the work.

Most of the twenty hours went on the ordinary work of a site: a mobile navigation sheet, button states measured in L*, a favicon, a mark that started as the Greek letter psi and ended as a signpost. Three decisions carried the most risk and taught the most.

Feature01/ 03

The field

From stock imagery to his actual data.

Every biotech consultancy opens on the same photograph of a pipette. Seth's opens on a UMAP embedding of real single-cell data, orbiting in three dimensions, and the services sit on a double-helix particle field. Both are hand-written canvas elements with no dependencies. Both nearly took the site down.

The detail↓

The problem.Seth asked for high-end visualization. The obvious move is a stock hero and a gradient. The honest move is to show the thing he actually does: cluster cells and read the result. A UMAP plot is the picture every single-cell scientist recognizes on sight, and to a buyer it reads as competence before a word is read.

The constraint was cost. Animated canvases are the fastest way to make a page stutter, and this one had to run under a sticky header on whatever laptop a lab director happens to have.

The solution.Two custom elements, umap-field and helix-card, each one canvas with its own animation loop, paused off-screen, still under reduced-motion. The UMAP orbits a real embedding; the orbit's outer swing was measured at 1.157× the 97th-percentile extent and the frame set so nothing ever leaves it.

The ceiling turned out to be two. An early cut gave each service card its own magnified field, five canvases in all, and the page stopped rendering reliably. I pulled the per-card instances the same day. The cards now sit over the section's single field at 72% opacity and go solid on hover, which reads as the same idea at a fifth of the cost.

“Two animated canvases render. Five do not. Everything in between is a question of what else is on the page.”
How I help, frosted cards over the helix field. Click to enlarge.
Feature02/ 03

Three buyers, one page

From a CV in prose to a signpost.

The site had to work for a lab director with a sample problem, a platform company weighing a launch, and an attorney who needs a scientist who can explain, without any of them feeling like an afterthought.

The detail↓

The problem.Seth's first draft of the About section was his CV rewritten as paragraphs: twenty years, three institutions, a list of platforms. Accurate, and what every consultant's site says. Anyone gets that from LinkedIn. A prospective client cannot tell from it whether Seth has seen their problem before.

The buyers also scan for different things: a lab director for their sample type, a platform company for someone who has stood in customer labs, an investor for evidence he has done diligence before. The headline had to carry all of them in one line, which is where the second session went: I walked Seth through how people actually read a page, top-left first and then down the left edge, and we worked the line out loud.

The solution.The headline went through “diamond in the rough to polished results” and two other metaphors before landing on “From challenging samples to polished results.” Direct beat clever every time we tested it against the reader we had in mind. The same rule cut a platform list down to the five he has genuinely run and reframed it as “hands-on with”, his phrase, because a list of instruments says he has heard of them and hands-on says he has used them.

Services are grouped by who is asking, not by what Seth does: single-cell workflows for R&D teams, emerging technologies for platform companies, litigation support for attorneys. That hierarchy was itself a session decision: the primary group as the first row of cards, the secondary groups below it, so size and position carry the priority instead of another layer of headings. Every card is the same shape, heading, one line of who it is for, a rule, three deliverables, so a reader who has found their group can stop looking.

The About copy went three rounds with Seth. What shipped is his own words cut from 172 to 131, opening with one line that says what he does before it says where he has been, and closing with three verbs in pills instead of a paragraph. The full CV moved to its own page with a downloadable PDF, minus his referees' phone numbers: they did Seth a favor, and a public page is not where to repay it.

“Direct beat clever every time we tested it against the reader we had in mind.”
About, lead line, biography and the three offers. Click to enlarge.
Feature03/ 03

The contact path

From a form that said "sent" to one that delivers.

Seth's one hard requirement was a form that reached a real inbox. It was the feature that looked most finished and was least.

The detail↓

The problem.My first test returned “your message is on its way” and nothing arrived. The honeypot field, parked off-screen to catch bots, had been autofilled by Chrome; the endpoint classified a human as a bot and discarded the message while reporting success. A form that lies about delivery is worse than no form.

Behind it sat three more failures in a row: an API key that had never been set, a destination address left over from week one that silently overrode the code, and a sender address that Microsoft 365 filed under junk.

The solution.The honeypot moved to display:none with the opt-out attributes for the four major password managers, and a tripped honeypot now logs loudly instead of vanishing. Environment variables are read at request time rather than baked in at build, after a cached redeploy shipped a stale value and made a correct dashboard change appear to do nothing.

Delivery runs through Resend, verified on Seth's own domain so mail arrives signed as psignpostconsulting.com. That verification had to go on a subdomain: the root already carried the SPF record his live Outlook mailbox depends on, and a second one would have hard-failed every email he sends.

“A form that says “sent” and does not send is not a bug in the form. It is a bug in the trust.”
Get in touch, the form and the availability line. Click to enlarge.

The honest part

The site shipped clean. Then Chrome started dropping the canvases.

Three days after launch Seth's screenshots showed the services field garbled into smears, then gone, with nothing in the console. I could not reproduce it. Everything I could measure, the renderer's own frames, the DOM, the scrim layers, said it was fine. It took three rounds. A pixel cap and a context-loss handler treated symptoms. The cause was ten blur filters per frame on a three-megapixel canvas, precisely the thing that pushes Chrome off its accelerated path until it drops the context. The blur is now baked into four sprite variants, both elements rebuild themselves if the context dies, and the header lost its backdrop blur. Zero filters in the paint loop, measured.

And two things I would say to a client up front.

The services section went from cards, to bubbles rising out of the helix, and back to cards in two days. The bubble idea was mine and it was good; making it real needed a canvas per card, and five canvases broke the page. I pulled it rather than ship it degraded. The logo took the same route in reverse. Seth wanted the Greek letter psi in it, for PSI, and knew the symbol means little to most of his buyers; a signpost reads to anyone. In one session we ran that brief through two image models and got a plausible 3D mark with yellow accents that neither of us quite believed. The one that shipped I drew in Figma in an afternoon, in the navy and cobalt he had picked, and it is the only element on the site no model touched. The tools are extraordinary at building and at measuring. Judgment about what looks right is still the job.

What I learned

The engineering, for anyone who wants it.

Some of these I noticed. Most I was taught, by the tools and by the things that broke. None of them are in the sections above because the client did not need them, but an engineer reading this would want to know.

Rendering and motion

  • Two animated canvases is the ceiling for a page like this. Five made it stop rendering. The number that matters is what else is compositing on top of them.
  • Never put a canvas filter in the paint loop. Ten blur calls per frame on a three-megapixel canvas pushed Chrome off its accelerated path until it dropped the context. Bake the blur into sprite variants once and pick one at draw time.
  • Cap a canvas by pixel area, not just by device pixel ratio. A full-bleed section at 2x was 7.3 million pixels repainted every frame for no visible gain over 3.2.
  • A lost GPU context is silent. Nothing in the console, nothing in the DOM, the loop keeps running and paints into nothing. Poll isContextLost and rebuild the element outright.
  • Backdrop blur over an animated canvas is a compositing trap. The header lost its blur and nothing was lost with it.

Delivery and plumbing

  • Static by default. Server code only where a request needs it. Here that was the contact endpoint and, during the build, a preview gate written to fail closed: lose its configuration and it locks everyone out rather than letting everyone in.
  • Build-time environment variables are baked in. A redeploy that reuses the build cache ships the old value, so a correct change in the dashboard appears to do nothing. Read process.env at request time.
  • Off-screen honeypots get autofilled. Chrome filled a field parked at left:-9999px and the endpoint binned a real message while reporting success. Use display:none, add the password-manager opt-out attributes, and log when it trips.
  • A domain gets one SPF record. The root already carried the one his live mailbox depends on, so the email service was verified on a subdomain. A second root record would have hard-failed every message he sends.
  • Strip the EXIF before a phone photo enters a public repo. His portrait carried the GPS coordinates of where it was taken. The image pipeline would have dropped them from the published files, but the source would have lived in git history forever.

Measuring design

  • Measure contrast on the glyphs, not the element box. Sampling the paragraph’s bounding box gave 5.94:1, a pass. Sampling the rendered letters gave 4.17:1, a fail. The second number was the true one.
  • A hover state needs about ten points of L* to register. Five is above the threshold of noticing but below the threshold of noticing without something to compare against. Light buttons should darken on hover; brightening toward white from 96 L* is a three-point move nobody sees.
  • text-shadow inherits. A shadow set on a hero container for legibility landed on the download button inside it and drew a smudge behind dark text on a light pill.
  • Tailwind 4 emits translate, not transform. A press nudge transitioned on transform snapped instead of easing. Name the property the framework actually writes.
  • One token per role. Fifteen uppercase labels across two pages were four different sizes and trackings doing one job. One eyebrow class, callers vary only the color.
  • A commit count measures how often work was saved. It says nothing about how much, how well, or how fast, which is why it is not on this page.