Building and Scaling AI-Powered Visual Search Infrastructure

figma · Tech · 2024 · source Agentic: no · promotional bias: low · source-backed · extracted with prompt 0.5.5 (schema v2)

Generated from extraction.json — do not edit by hand. Every claim cites verbatim source evidence; stated means the quote directly asserts the claim, implied means it supports it indirectly.

Problem

Domain: Design search infrastructure

Figma needed users to find visually or semantically similar designs and components across large teams and organizations, even when frames were unlabeled or component names did not contain the user's keywords.

Constraints:

Architecture

Figma built AI-powered search around CLIP-style multimodal embeddings. Server-side asynchronous jobs run a headless C++ Figma editor to identify indexable frames, persist metadata and embeddings in DynamoDB, render thumbnails to S3, generate batched embeddings through AWS SageMaker, and write vectors plus metadata into OpenSearch. Search queries from text, screenshots, or Figma selections are embedded on demand and matched against the vector index. Component search runs lexical and embedding searches in parallel, normalizes and boosts scores, and interleaves results. Scaling work focused on C++ preprocessing, CPU thumbnail rendering, four-hour indexing debouncing, autoscaling, index pruning, vector quantization, and OpenSearch bug fixes.

Diagram confidence: explicit (topology described by the source).

flowchart LR
  search-ui[Search UI] --> query-embedding-generator[Query embedding generator]
  query-embedding-generator --> clip-embedding-model[CLIP embedding model]
  query-embedding-generator --> opensearch-vector-index[OpenSearch vector index]
  frame-identification-job[Frame identification job] --> dynamodb-metadata-store[DynamoDB metadata store]
  frame-identification-job --> thumbnail-renderer[Thumbnail renderer]
  thumbnail-renderer --> s3-thumbnail-store[S3 thumbnail store]
  s3-thumbnail-store --> sagemaker-embedding-endpoint[SageMaker embedding endpoint]
  clip-embedding-model --> sagemaker-embedding-endpoint
  sagemaker-embedding-endpoint --> opensearch-vector-index
  dynamodb-metadata-store --> opensearch-vector-index
  opensearch-vector-index --> hybrid-result-ranker[Hybrid result ranker]
  lexical-component-index[Lexical component index] --> hybrid-result-ranker
  hybrid-result-ranker --> search-ui
  indexing-scheduler[Indexing scheduler] --> frame-identification-job
  indexing-scheduler --> opensearch-vector-index
Drag to pan · scroll or use +/− to zoom · reconstructed from the source's described component topology

Components

Component Role Interacts with
Search UI Figma surfaces for Search for designs and Search for components, accepting text descriptions, screenshots, and layer selections. Query embedding generator, Hybrid result ranker
Query embedding generator Generates on-the-fly embeddings for screenshots, text, or rendered layer selections. Search UI, CLIP embedding model, OpenSearch vector index
CLIP embedding model Open-source multimodal embedding model mapping images and text into a shared vector space. Query embedding generator, SageMaker embedding endpoint
Frame identification job Asynchronous job running the headless C++ Figma editor to enumerate indexable frames. DynamoDB metadata store, Thumbnail renderer
Thumbnail renderer Renders frame thumbnails using CPU-based llvmpipe on newer AWS instances. Frame identification job, S3 thumbnail store
DynamoDB metadata store High-throughput key-value store for frame metadata and embeddings used during indexing and updates. Frame identification job, OpenSearch vector index
S3 thumbnail store Stores rendered thumbnails that later feed batched embedding generation. Thumbnail renderer, SageMaker embedding endpoint
SageMaker embedding endpoint Batched inference service for image embeddings, with parallel downloading, resizing, and normalization. CLIP embedding model, S3 thumbnail store, OpenSearch vector index
OpenSearch vector index Vector search index holding embeddings plus faceted metadata for nearest-neighbor design and component retrieval. Query embedding generator, SageMaker embedding endpoint, DynamoDB metadata store, Hybrid result ranker
Lexical component index Pre-existing fuzzy string search over component names and descriptions. Hybrid result ranker
Hybrid result ranker Normalizes lexical and semantic result scores, boosts exact matches, and interleaves results. Search UI, OpenSearch vector index, Lexical component index
Indexing scheduler Controls freshness through debounced indexing and cluster autoscaling. Frame identification job, OpenSearch vector index

Data flow

For indexing, an asynchronous job uses the server-side C++ editor to enumerate searchable frames in a Figma file, persists frame metadata to DynamoDB, renders thumbnails to S3, and queues embedding generation. SageMaker receives batches of thumbnail URLs, downloads and normalizes images in parallel, and emits embeddings that are persisted and indexed in OpenSearch with file, project, team, and organization metadata. At query time, Figma converts a text prompt, screenshot, or layer selection into an embedding and performs nearest-neighbor vector search; component search also runs fuzzy lexical search and interleaves normalized results.

4 supporting quotes
“To identify them, we run a headless server-side version of the C++ Figma editor in an asynchronous job. [![An orange worm trying to escape from a castle wall laden with”article, stated
“Once we’ve identified all of the current indexable designs in a Figma file, the frames’ metadata is persisted to DynamoDB. Thumbnails are rendered and uploaded to S3.”article, stated
“Embedding requests are sent to our SageMaker endpoint in batches, allowing us to perform inference in parallel.”article, stated
“Embeddings are written to the search index, along with additional metadata like the frame’s name, the containing file ID and name, and the containing project, team, and organization of the frame.”article, stated

Why this architecture

Figma chose multimodal embeddings because users can express visual intent through words, screenshots, or Figma selections, and CLIP maps those modalities into one vector space. The pipeline is split into asynchronous stages because frame enumeration, thumbnailing, embedding generation, and indexing need different batching and retry controls. OpenSearch was reused because it already powered Figma search, while hybrid ranking preserved exact lexical expectations. Cost controls were built into preprocessing and index freshness because indexing billions of entries made preprocessing and OpenSearch footprint more expensive than the model alone.

4 supporting quotes
“The model can take multiple forms of inputs (image and text) and output embeddings that are in the same space.”article, stated
“Separating the individual steps of the pipeline into discrete jobs gives us more precise control over batching and retry behavior.”article, stated
“OpenSearch is already deployed widely across Figma for traditional search features, so it made sense to leverage OpenSearch for embedding search at Figma as well.”article, stated
“Enabling AI-powered search at Figma’s scale requires generating embeddings for and indexing billions of entries.”article, stated

Models used: CLIP · fine-tuned CLIP variant for component search

Scale

Alternatives considered (roads not taken)

Textual JSON representation of a Figma selection

Rejected because: Image embeddings returned better results and shared the screenshot-search code path.

“we experimented with generating an embedding from a textual representation of your selection (e.g. JSON), but we found that generating embeddings via images returned better results”article, stated

RDS for AI search metadata

Rejected because: The feature needed high-throughput key-value reads/writes, not transactions or relational constraints.

“the AI search features make use of DynamoDB to store metadata and embeddings. The features require only a simple key-value store”article, stated

GPU thumbnail rendering on older AWS instances

Rejected because: CPU software rendering with llvmpipe on newer instances was cheaper and faster for this workload.

“We moved our thumbnailing from GPU-based rendering on an older AWS instance type to CPU-based rendering with llvmpipe on a newer instance type.”article, stated
“This was a huge cost saver; the CPU instance type machines are much cheaper, and since they’re newer they get through our workload more quickly.”article, stated

Trigger indexing on every file change

Rejected because: Frequent editing sessions made immediate reindexing wasteful; four-hour debouncing preserved freshness while cutting work.

“Our identification and indexing pipeline was originally written to be triggered off of every change in a file.”article, stated
“if we debounced indexing to be at most every four hours, we would only have to process 12% of the data! * Cluster autoscaling. Figma usage is heavily diurnal. Scaling down our clusters during lower traffic periods helped us avoid paying for unnecessary”article, stated

Pure semantic component search replacing lexical search

Rejected because: Figma retained valuable exact and fuzzy string matches and interleaved them with semantic results.

“In order to safely roll out AI-powered search over components and to retain valuable lexical results, searches are performed simultaneously against both the lexical index and the new embeddings index.”article, stated

Patterns

hybrid-search (retrieval)

Component search runs lexical and embedding indexes in parallel, normalizes scores, boosts exact lexical matches, and interleaves results.

“searches are performed simultaneously against both the lexical index and the new embeddings index.”article, stated
“the result sets are assigned new scores based on a min-max normalization, with exact lexical matches receiving a boost.”article, stated

retrieval-ranking (search)

Figma uses nearest-neighbor vector search over embeddings with faceted metadata and a score normalization layer for hybrid results.

“issuing a query to find the items whose embeddings are nearest to the query.”article, stated
“This additional metadata lets us support faceted search (filters) in addition to the nearest neighbor vector search.”article, stated

batch-offline-processing (indexing)

Frame identification, thumbnailing, embedding generation, and search indexing are separate asynchronous jobs with batched SageMaker inference.

“This identification and thumbnailing job queues the next job and terminates successfully. Separating the individual steps of the pipeline into discrete jobs”article, stated
“Embedding requests are sent to our SageMaker endpoint in batches, allowing us to perform inference in parallel.”article, stated

privacy-preserving-processing (data-governance)

The embedding models were not trained on private customer files; fine-tuning used public Community UI content.

“Figma’s embedding models were not trained on any private Figma files or customer data. Fine-tuning was performed with images of user interfaces from public, free Community files.”article, stated

cost-aware-routing (operations)

The indexing pipeline reduces work through debounce windows, C++ preprocessing, CPU rendering, autoscaling, index pruning, and vector quantization.

“A huge part of the project was focused on optimizations to keep costs down.”article, stated
“we debounced indexing to be at most every four hours, we would only have to process 12% of the data!”article, stated

Tradeoffs

Model input representation

Chose image-based embeddings for selections over textual JSON representations of selected layers.

Rationale: Image embeddings produced better results and reused the screenshot search path.

“generating embeddings via images returned better results and ensured we were sharing code paths when searching via a screenshot.”article, stated

Vector infrastructure

Chose OpenSearch over introducing a new specialized vector database.

Rationale: OpenSearch was already widely deployed at Figma for search, making it operationally attractive despite later kNN quirks.

“OpenSearch is already deployed widely across Figma for traditional search features, so it made sense to leverage OpenSearch for embedding search at Figma as well.”article, stated

Search-result strategy

Chose hybrid lexical plus semantic search over semantic-only component search.

Rationale: Hybrid search kept valuable exact and fuzzy lexical results while adding semantic understanding.

“to safely roll out AI-powered search over components and to retain valuable lexical results, searches are performed simultaneously against both the lexical index and the new embeddings index.”article, stated

Index freshness

Chose debounced indexing at most every four hours over reindexing on every file change.

Rationale: Users make many edits in a session, so fast refreshes had little value compared with the processing cost.

“Rapidly refreshing the AI-powered index isn’t necessary on every single change, but we do want to keep our index reasonably fresh.”article, stated

Index footprint

Chose vector quantization over full four-byte float embeddings everywhere.

Rationale: Quantization reduces memory required for storage and search while accepting a small nearest-neighbor accuracy reduction.

“vector quantization](https://opensearch.org/docs/latest/search-plugins/knn/knn-vector-quantization/) is a technique to compress the size of embeddings to reduce the memory required to store and search them, at the cost of a small reduction”article, stated

Gotchas (what bit them)

The expensive part of embedding search was not necessarily model inference.

Root cause: Identifying meaningful designs inside Figma files and rendering thumbnails dominated compute costs.

Resolution: Rewrite identification from Ruby to C++, remove JSON serialization, and move thumbnailing to cheaper CPU software rendering.

“we were able to identify that the major compute costs were being driven not by embedding generation, but by identifying and thumbnailing meaningful designs within Figma files.”article, stated

A tiny beta cohort could still require nearly all teams to be indexed.

Root cause: For any one user to experience search correctly, the user's entire team data had to be indexed, and Figma has many small teams.

Resolution: Make indexing and backfilling cost-efficient before broad rollout through preprocessing and index-size optimizations.

“with even a small percentage of users onboarded, we’d quickly converge on having to index almost all teams at Figma—most of our teams are small and there are many of them!”article, stated

Batching inference past a threshold increased latency linearly.

Root cause: The SageMaker embedding endpoint did not keep delivering sublinear batching benefits at larger batch sizes.

Resolution: Experiment to find the optimal batch size and parallelize image download, resizing, and normalization.

“Determining the optimal batch size required some experimentation; past some threshold we started to see latency growing linearly with batch size, instead of a sublinear batching effect.”article, stated

Replica shards returned nondeterministic vector results.

Root cause: OpenSearch replica queries hit a type-casting error in the delete path on clusters using segment replication.

Resolution: Partner with AWS OpenSearch to diagnose the bug and ship an upstream fix.

“queries routed to replicas in OpenSearch were returning non-deterministic results compared to those routed to primaries.”article, stated
“the OpenSearch team helped determine that this type casting error in the delete path was affecting replicas on clusters that utilize segment replication and shipped a fix”article, stated

Removing embeddings from OpenSearch _source broke metadata updates.

Root cause: OpenSearch used _source to apply document updates, so updated documents lost embeddings that had been removed from _source.

Resolution: Re-fetch embeddings from DynamoDB on updates while keeping the _source optimization for normal query responses.

“after removing embeddings from _source, everytime we tried to update a document (e.g. like when file names change), we were accidentally wiping the embedding off of the document”article, stated

Evaluation

Figma relied on end-to-end testing, batch-size experiments, cost/latency analysis, and debugging of distributed OpenSearch behavior. The source emphasizes operational testing more than offline relevance metrics.

Metrics: embedding batch latency · indexing data volume · OpenSearch result determinism · index size · compute cost · query latency

“Determining the optimal batch size required some experimentation; past some threshold we started to see latency growing linearly with batch size”article, stated
“We noticed some periodic non-determinism when testing our search features end-to-end.”article, stated

Outcomes

The system was built for billions of embeddings and organization/team-wide indexing, with continued rollout planned after Config 2024.

3 supporting quotes
“At Config 2024, we introduced new AI-powered search features that allow you to search across all of the designs and published components in a team or organization.”article, stated
“Now, Assets search offers an AI-powered semantic understanding of the component.”article, stated
“These AI search features are currently in early beta and will be continuing to roll to more users over the coming months.”article, stated