Heart Stream

ProShowcase & Media3DWebGLpowered by @vectorvesper/motion

Testimonials ride toward you on procedural 3D shapes — pebbles, sparkles, speech bubbles, hex tokens or hearts — turning to face you as they reach reading distance. Hover one and it flies to the front, flips, and holds still while you read it. Five shape styles, switchable from a dock or driven from your own UI.

Heart Stream

Included with membership

Members install Heart Stream with one command and get the full source, the usage code and the Framer remix, licensed for client work. One membership covers the whole library and the CLI.

When to use it

A social-proof section that should feel like a place rather than a list — a landing page hero, a customers page, the stretch between a pitch and a pricing table. It wants the full width of the screen and a good deal of height; it is a scene, not a card. Reach for it when you have a lot of testimonials and no intention of making anyone read all of them.

  • Define items outside the component, or wrap it in useMemo. A new array on every render rebuilds the entire scene — every shape, and one large texture per testimonial.
  • Give it real height. It fills its container and enforces a 680px floor, so a short parent just clips the scene.
  • Keep quotes to a sentence or two. Each one is drawn into a fixed square, and long copy sets smaller and smaller until it runs off the bottom.
  • Set accentColor per item. It tints the metric and the ring around the author photo, and it is the only per-item colour that reads at a glance.
  • Use square author photos served from your own domain. They are drawn into the face, and a cross-origin image that refuses to load leaves an empty circle.

Avoid:

  • Do not put it in a narrow column or a short panel. Shapes spawn across a spread computed from the container's own proportions, and a tall thin box packs them into a corridor.
  • Do not raise streamCount much past the default. Every element is a separate object with its own materials, and the cost is paid whether or not it is on screen.
  • Do not hide the configurator without driving shape yourself — showConfigurator={false} on its own leaves the shape fixed at whatever it mounted with.
  • Do not set a dark backgroundColor and leave the title alone. The heading is dark by default; pass titleClassName to lift it.
  • Do not treat the quotes as content anyone will read in order. Shapes arrive in a shuffled, recycled stream — it is atmosphere with detail available on demand, not a testimonial list.

Props

Testimonials fly toward you on solid 3D shapes. Each one drifts up out of the distance, turns to show its quote as it reaches reading depth, then passes the camera and is recycled to the back. Hover one and it comes to the front, flips over and holds still while you read it; everything else steps back to get out of the way. Five shape styles ship with it, switchable from a dock along the bottom.

<HeartStreamTestimonial />

The scene, its heading and the shape dock. Paints its own background and fills its container, so treat it as a section.

PropTypeDefaultDescription
items*HeartTestimonialItem[]—The testimonials. Required. They are cycled through the pool, so fewer items than shapes just means repeats.
shape"squircle" | "sparkle" | "bubble" | "token" | "heart""squircle"Which shape the testimonials ride on. Sets the starting shape, and changing it later switches the scene over.
shapeColorstring—Front-face colour for every shape, overriding the per-shape default. An item's own shapeColor wins over this.
shapeRimColorstring—Colour of the bevelled edge. Set it a few steps darker than shapeColor — that contrast is what gives the shape its thickness.
backgroundColorstring"#f5f4ef"Colour behind the scene. Also tints the depth haze, so distant shapes fade into it rather than sitting on top of it.
showConfiguratorbooleantrueRenders the shape-picker dock along the bottom. Turn it off for a fixed look, and drive shape yourself if you want it to change.
streamCountnumberitems.length × 3, clamped to 30–44How many shapes are in flight at once. More fills the depth; each one is a separate object, so this is the main cost dial.
flightSpeednumber0.28How fast the stream travels toward you. Adjustable live — changing it does not restart the scene.
titlestring"Loved by Fast-Moving Engineering Teams"Heading across the top of the scene.
titleClassNamestring""Applied to the heading. Needed for a dark backgroundColor — the heading is dark by default.
classNamestring""Applied to the wrapper.
styleReact.CSSProperties—Inline styles merged onto the wrapper, after backgroundColor.
disableAdaptiveQualitybooleanfalseKeeps edge smoothing on and the pixel ratio uncapped on every device. By default the runtime turns smoothing off and caps the ratio on lower tiers, which shows most on the shape edges.
mount"auto" | "eager""auto"When to build the scene. The default waits until the page has frame headroom to spare. Set "eager" to start immediately — the shapes stream from depth, so a late start is visible as an empty stage. Note this defaults the opposite way from the components that build a 3D scene: those start eagerly and take "auto" to defer. Both mean the same thing by "auto" — let the runtime pick the moment — the default just matches what each component already did. Neither setting takes it off the shared frame loop or overrides the reduced-motion preference.

<HeartTestimonialItem />

type

One testimonial. Everything through metricValue is required — the face has a slot for each and draws them every time.

PropTypeDefaultDescription
id*string—Stable key. Also what the face texture is cached under, so it must be unique.
company*string—Company name, set in caps at the top of the face.
quote*string—The testimonial. A sentence or two — it wraps to a fixed width.
authorName*string—Who said it.
authorRole*string—Their title and organisation.
authorAvatar*string—Author photo, drawn into a circle. Square images crop best; a cross-origin image that will not load leaves the circle empty.
metricValue*string—The headline number, such as "-68% Latency". Set in the accent colour under the company name.
accentColorstring"#e11d48"Tints the metric and the ring around the author photo. The per-item brand colour.
shapeColorstring—Front-face colour for this item's shapes only. Overrides the component-level shapeColor.
shapeRimColorstring—Bevel colour for this item's shapes only.

<StreamShapeType />

type

The five shapes: "squircle" (a rounded pebble), "sparkle" (a four-point star), "bubble" (a speech bubble with a tail), "token" (a hexagon) and "heart". Each carries its own default colours and its own text layout, so the quote stays inside the silhouette.

It reads the visitor's reduced-motion preference and stops the flythrough — motion coming straight at the viewer is the kind most likely to cause trouble. The shapes hold their positions, and hovering one still brings it forward to read, because that is navigation rather than decoration.
The real cost is the faces, not the shapes: each testimonial gets one high-resolution texture drawn once and kept for as long as the scene lives. Thirty items is tens of megabytes of graphics memory. streamCount controls how many objects are in flight and is the dial to turn first; the shape dock rebuilds the scene each time it is used, which is why it belongs in a demo more than in production. Rendering also steps down on weaker hardware — edge smoothing and pixel density both drop a level.
Client component. It opens a 3D drawing context and draws the faces on a canvas at mount — import it dynamically with ssr: false if your route renders on the server.
It draws on the shared frame loop rather than starting one of its own, so putting several animated components on a page costs one callback, not one each, and the scheduler can set decorative work aside when frames get tight. Edge smoothing and pixel ratio follow the device tier, decided once for the page.
Back to all components