# Sunoverse frontend

*William Feng\
August 29, 2025*

Read [the backend README](../../../../../../suno_utils/suno_utils/worker/sunoverse/README.md) first for an overview of how the points get processed and formatted for the CDN.

*This file is pretty short because most stuff in the frontend is self-explanatory and LLMs are pretty good at answering questions about how stuff works.*

**tl;dr:** main logic is in [`SunoverseClient.tsx`](./SunoverseClient.tsx)

## Deepscatter

Initially I wanted to write my own rendering library using WebGL, but turns out this would have been way too time-consuming so now we use [deepscatter](https://github.com/nomic-ai/deepscatter). It's pretty complicated but in essence is tightly integrated with `quadfeather` (the thing we use to convert sets of points to tiles) to provide zero-copy GPU rendering of up to billions of points as a scatterplot in the browser.

I copied the deepscatter library into this repo under `ui/app-ui/src/app/(root)/sunoverse/deepscatter` because a few things needed to be tweaked to fit the Sunoverse use case.

The core of the flow is that a `Scatterplot` instance is created, which acceps as initialization arguments:
1.  an endpoint from which to fetch `.feather` tiles
2.  set of labels with x, y coordinates

## Highlights

When the user hovers over a point, deepscatter does some clever WebGL stuff to figure out which point you hover over. I modified `interaction.ts` to allow `SunoverseClient.tsx` to add a callback to hover. This is how we get tooltips (see [`SongTooltip.tsx`](./SongTooltip.tsx) for the tooltip component) and highlight circles.

Circles have a `hoverTransient` property that defines whether they should only exist while the user hovers over their point as well as a `clickTransient` property that defines whether they should exist only while the user has not clicked on another point.

This component is defined in [`Highlights.tsx`](./Highlights.tsx). There's more to be said here, but I'm running low on time so would recommend reading the source :)

## Rendering issues on local

Initially, I ran into an issue on local with React hot module reloading where two instances of the scatterplot would initialize and cause flickering.

This was fixed by having a global singleton instance. However, this caused the page to break on reload or HMR, so on August 28 I did a bunch of stuff with Claude to finally fix React state management/lifecycle/timing stuff and now it should be pleasant to iterate on Sunoverse in local.

## Locate myself

See the [studio_api backend endpoints]() for more info on how this works. tl;dr:
1. Server receives a request to embed a user
2. Gets user ID and queries RDS for 128D ditto genre embeddings of users's public songs
3. Calls `sunoverse-worker` Modal worker, class `UMAPWorker` to get the median point of all these embeddings ([Wikipedia: Geometric Median](https://en.wikipedia.org/wiki/Geometric_median)) projected from 128D to 2D. (See `modal_worker_sunoverse` for implementaiton details.)
4. Plots on frontend using `Highlights.tsx`.

## Find genre

Allows users to find the location of arbitrary texts. Flow:
1. User enters a string in "find genre" box
2. Search through JSON file `genre_umap_embs.json` to see if the string has already been preprocessed. If so, zoom in on that point
3. Else, send to `UMAPWorker` to embed the string in 128D and then use the UMAP to project down to 2D.
