Skip to content

Repository files navigation

alchem.is

alchemis

Live demo

Sketch a molecule or pull one from PubChem, then predict how cancer cells respond to it across 985 cell lines. This repo (deepresponse-ui) is the web front end: a chemistry editor, a molecule search, and a results table over a trained model.

Why I built it

I made this for a Pharmacogenomics course at UBA (Facultad de Farmacia y Bioquímica). Drug response screening usually means lab time and tooling that is nowhere near a browser. I wanted the fast version: draw a compound, hit predict, and see a ranked table of cancer cell lines with a potency estimate for each. The model behind it is trained on GDSC data and runs as a separate service. What lives here is the interface: the molecule handling and the calls out to the model.

What it does

  • Draw a molecule in a full Ketcher editor and get its SMILES automatically
  • Search PubChem by name with live autocomplete, and load the compound straight into the canvas
  • Predict pIC50 across 985 cancer cell lines from a single SMILES string
  • Filter and sort the results by tissue and cancer type, with a potency label per row
  • Export the ranked table to PDF, with the molecule, the applied filters and a timestamp on the report
  • A walkthrough of the model itself: what pIC50 means, the two encoders, how it was trained and what it cannot tell you

How it works

It is a Next.js app. The molecule editor runs entirely in the browser and PubChem is called straight from the client. Prediction goes through one route of its own (app/api/predict/route.ts), which forwards the request to a FastAPI model hosted on Modal.

flowchart LR
  U[Draw or type] --> K[Ketcher editor]
  S[Search box] -->|name| PC[PubChem]
  PC -->|CID then SMILES| K
  K -->|SMILES| R["/api/predict route"]
  R -->|server side| API[DeepResponse model on Modal]
  API -->|pIC50 x 985 cell lines| T[Filterable results table]
Loading

Resolving a name to a structure

Turning a name into a structure takes two hops through PubChem. As you type (debounced at 500 ms so it does not fire on every keystroke) it hits PubChem's autocomplete for compound names. Pick one and it runs the resolution: name to CID, then CID to SMILES. It asks for several SMILES flavors and takes the first that comes back (Canonical, then Isomeric, then Connectivity). That chain is why typing "imatinib" and clicking a suggestion drops a real, drawable structure onto the canvas.

Keeping the drawing and the string in sync

Ketcher is loaded with ssr: false and a StandaloneStructServiceProvider, so all the structure parsing happens in the browser through Ketcher's WASM. There is no chemistry server to run. The SMILES string and the canvas can each change the other. PubChem sets the SMILES, which must redraw the canvas, and drawing on the canvas must update the SMILES. Left alone, that loops. The editor guards it with refs. They track whether an update came from the editor itself and what the last SMILES was. So an external SMILES gets pushed into the canvas without bouncing back out as a fresh change event.

flowchart LR
  P[PubChem sets SMILES] --> S[SMILES state]
  S -->|redraw| K[Ketcher canvas]
  K -->|edit emits SMILES| S
  G[Ref guard] -.->|blocks echo| S
Loading

From SMILES to a prediction

The client cleans the SMILES before sending. Ketcher can emit CXSMILES with an extension block after a |, and the model wants a plain string, so it splits that off first. The cleaned string is POSTed to /api/predict, which forwards it to Modal from the server. That hop exists because the model endpoint sends no Access-Control-Allow-Origin header, so a browser cannot call it directly. Back comes a pIC50 for every one of the 985 cell lines, each with its tissue and cancer type. There is also a linear calibration hook (pIC50 = a * raw + b) that can stretch and shift the raw output. It is off by default, so what you see is the model's own numbers.

Staying inside PubChem's rate limit

PubChem allows five requests per second per IP and answers 503 above that. A page of ten result cards would otherwise fire around twenty-one requests at once: the autocomplete, one name-to-CID lookup per card, and ten structure thumbnails. Lookups run three at a time, every call retries 503 and 429 with an exponential backoff, and picking a card retires the in-flight formula lookups so the request you are waiting on gets the budget.

Refusing to send a structure that is not a molecule

The SMILES field takes free text, so it can hold something unparseable. Ketcher reports back whether it managed to build a molecule from the current string, and Predict stays disabled while it could not. Without that, an invalid structure costs a full round trip to the model before failing.

Potency bands tuned to the model's real range

The model's pIC50 outputs cluster in a narrow band, roughly 4.7 to 6.6. The activity labels (Very Potent down to Very Weak) use thresholds fitted to that observed range.

Tech stack

Frontend: Next.js 16, React 19, TypeScript, Tailwind CSS v4 Chemistry: Ketcher (ketcher-react, ketcher-standalone, indigo-ketcher), smiles-drawer Data: PubChem REST (autocomplete, name to CID, CID to SMILES) Model: a DeepResponse FastAPI service on Modal, trained on GDSC, returning pIC50 per cell line

Layout

alchem.is/
  app/
    page.tsx                   search, editor, predict, and the results table
    api/predict/route.ts       same-origin proxy to the Modal endpoint
    components/
      KetcherEditor.tsx        Ketcher with two-way SMILES sync
      ModelExplainer.tsx       how the model works, with data-scaled diagrams
      MolecularBackground.tsx  procedurally generated skeletal structures

Running it

npm install
npm run dev            # http://localhost:3000

The prediction endpoint is set in app/api/predict/route.ts. Point it at your own model service if you are running the DeepResponse backend yourself.

Status

Working front end. The model that produces the predictions lives on Modal and is not in this repo, so this build depends on that service being up. A prediction takes roughly 40 to 60 seconds because it scores all 985 cell lines in one request.

About

Draw a molecule, predict its effect across 985 cancer cell lines. Ketcher editor and PubChem search over a GDSC-trained model.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages