
Spline Interactive
- 1.7k installs
- 629 repo stars
- Updated November 20, 2025
- freshtechbro/claudedesignskills
spline-interactive is an agent skill that browser-based 3d design tool with visual editor, animation, and web export. use this skill when creating 3d scenes without code, designing interactive web experiences, prototypin
About
spline-interactive is an agent skill from freshtechbro/claudedesignskills that browser-based 3d design tool with visual editor, animation, and web export. use this skill when creating 3d scenes without code, designing interactive web experiences, prototyping 3d ui, exporting to . # Spline Interactive - Browser-Based 3D Design and Animation ## Overview Spline is a browser-based 3D design and animation platform that enables creators to build interactive 3D experiences without requiring code or specialized software knowledge. It provides a collaborative visual editor for designing, animating, and exporting 3D scenes across m Developers invoke spline-interactive during build/frontend work for frontend development tasks. The skill documents triggers, prerequisites, and step-by-step workflows grounded in SKILL.md. Compatible with Claude Code, Cursor, and Codex agent runtimes that load marketplace skills. Review the Security Audits panel on this listing before installing in production environments.
- Spline Interactive - Browser-Based 3D Design and Animation
- Visual 3D modeling with parametric shapes, extrusion, and boolean operations
- State-based animation system with timeline controls
- Interactive event system (mouse, keyboard, collision, scroll)
- Multiple export options (React components, web code, public URLs)
Spline Interactive by the numbers
- 1,685 all-time installs (skills.sh)
- +108 installs in the week ending Jul 29, 2026 (Skillselion tracking)
- Ranked #275 of 2,244 Frontend Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
spline-interactive capabilities & compatibility
- Capabilities
- spline interactive browser based 3d design and · visual 3d modeling with parametric shapes, extru · state based animation system with timeline contr · interactive event system (mouse, keyboard, colli · multiple export options (react components, web c
- Use cases
- orchestration
What spline-interactive says it does
- Visual 3D modeling with parametric shapes, extrusion, and boolean operations
- State-based animation system with timeline controls
- Interactive event system (mouse, keyboard, collision, scroll)
npx skills add https://github.com/freshtechbro/claudedesignskills --skill spline-interactiveAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.7k |
|---|---|
| repo stars | ★ 629 |
| Security audit | 2 / 3 scanners passed |
| Last updated | November 20, 2025 |
| Repository | freshtechbro/claudedesignskills ↗ |
What it does
Browser-based 3D design tool with visual editor, animation, and web export. Use this skill when creating 3D scenes without code, designing interactive web experiences, prototyping 3D UI, exporting to
Who is it for?
Developers working on frontend development during build tasks.
Skip if: Tasks outside Frontend Development scope described in SKILL.md.
When should I use this skill?
Browser-based 3D design tool with visual editor, animation, and web export. Use this skill when creating 3D scenes without code, designing interactive web experiences, prototyping 3D UI, exporting to
What you get
Completed frontend development workflow aligned with SKILL.md steps.
- Generated React project
- Spline component files
- Event handler examples
Files
Spline Interactive - Browser-Based 3D Design and Animation
Overview
Spline is a browser-based 3D design and animation platform that enables creators to build interactive 3D experiences without requiring code or specialized software knowledge. It provides a collaborative visual editor for designing, animating, and exporting 3D scenes across multiple platforms.
Key Features:
- Visual 3D modeling with parametric shapes, extrusion, and boolean operations
- State-based animation system with timeline controls
- Interactive event system (mouse, keyboard, collision, scroll)
- Multiple export options (React components, web code, public URLs)
- Real-time collaboration with team libraries
- AI-powered generation (3D from text, textures, style transfer)
- Built-in physics and particle systems
When to Use This Skill:
- Creating 3D web experiences without writing Three.js code
- Designing interactive product showcases or configurators
- Prototyping 3D UI/UX concepts visually
- Building marketing pages with 3D elements
- Collaborating with designers on 3D content
- Exporting scenes for React or vanilla JS integration
Alternatives:
- Three.js (threejs-webgl): For developers who prefer code-first approach and need maximum control
- Babylon.js (babylonjs-engine): For game-focused projects with built-in physics
- React Three Fiber (react-three-fiber): For React developers who want to build 3D with JSX
Core Concepts
1. Scene Structure
Spline organizes projects into scenes containing:
- Objects: 3D models, shapes, text, images
- Lights: Directional, point, spot lights
- Cameras: Orbital, perspective, orthographic
- Events: Interaction triggers
- States: Animation keyframes
2. Components System
Reusable elements that can be:
- Created from any object or group
- Instantiated multiple times
- Updated across all instances
- Overridden per instance
3. State-Based Animation
Animations are defined as transitions between states:
- Default State: Initial appearance
- Additional States: Target appearances
- Events: Triggers that cause state transitions
- Transitions: Duration, easing, properties
4. Interactivity Model
Event-driven system with:
- Events: User actions or scene triggers
- Conditions: Logic gates (if/else)
- Actions: State changes, audio, scene switches
- Variables: Dynamic data from APIs or user input
5. Export Options
Multiple deployment methods:
- Public URL: Direct shareable link
- Code Export: React component or vanilla JS
- Spline Viewer: Embedded iframe
- Self-Hosted: Download and host independently
Common Patterns
Pattern 1: Basic React Integration
Use Case: Embed a Spline scene in a React application
Implementation:
# Installation
npm install @splinetool/react-spline @splinetool/runtimeimport Spline from '@splinetool/react-spline';
export default function Hero() {
return (
<div style={{ width: '100%', height: '600px' }}>
<Spline scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode" />
</div>
);
}Key Points:
- Scene URL comes from Spline export dialog
- Component fills parent container
- Automatically handles loading and rendering
Pattern 2: Event Handling and Object Interaction
Use Case: Respond to user clicks on specific objects
Implementation:
import Spline from '@splinetool/react-spline';
export default function InteractiveScene() {
function onSplineMouseDown(e) {
// Check if clicked object is the button
if (e.target.name === 'Button') {
console.log('Button clicked!');
// Get object properties
console.log('Position:', e.target.position);
console.log('Rotation:', e.target.rotation);
console.log('Scale:', e.target.scale);
}
}
function onSplineMouseHover(e) {
if (e.target.name === 'Button') {
console.log('Hovering over button');
}
}
return (
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
onSplineMouseDown={onSplineMouseDown}
onSplineMouseHover={onSplineMouseHover}
/>
);
}Available Event Handlers:
onSplineMouseDown- Mouse press on objectonSplineMouseUp- Mouse releaseonSplineMouseHover- Mouse over objectonSplineKeyDown- Keyboard pressonSplineKeyUp- Keyboard releaseonSplineStart- Scene loaded and startedonSplineLookAt- Camera look-at eventonSplineFollow- Camera follow eventonSplineScroll- Scroll event
Pattern 3: Programmatic Object Control
Use Case: Modify object properties from React code
Implementation:
import { useRef } from 'react';
import Spline from '@splinetool/react-spline';
export default function ProductViewer() {
const cube = useRef();
const splineApp = useRef();
function onLoad(spline) {
// Save Spline instance
splineApp.current = spline;
// Find object by name
const obj = spline.findObjectByName('Product');
// Or by ID
// const obj = spline.findObjectById('8E8C2DDD-18B6-4C54-861D-7ED2519DE20E');
cube.current = obj;
}
function rotateProduct() {
if (cube.current) {
// Rotate 45 degrees around Y axis
cube.current.rotation.y += Math.PI / 4;
}
}
function changeColor() {
if (cube.current) {
// Change material color (hex color)
cube.current.material.color.set(0xff6b6b);
}
}
function moveProduct() {
if (cube.current) {
cube.current.position.x += 50;
cube.current.position.y += 10;
}
}
return (
<div>
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
onLoad={onLoad}
/>
<div style={{ position: 'absolute', top: 20, left: 20 }}>
<button onClick={rotateProduct}>Rotate</button>
<button onClick={changeColor}>Change Color</button>
<button onClick={moveProduct}>Move</button>
</div>
</div>
);
}Object Properties You Can Modify:
position- { x, y, z }rotation- { x, y, z } (radians)scale- { x, y, z }material.color- Color hex valuevisible- Boolean
Pattern 4: Triggering Spline Animations
Use Case: Trigger animations defined in Spline from React
Implementation:
import { useRef } from 'react';
import Spline from '@splinetool/react-spline';
export default function AnimatedCard() {
const splineApp = useRef();
function onLoad(app) {
splineApp.current = app;
}
function triggerHoverAnimation() {
// Emit mouseHover event on 'Card' object
splineApp.current.emitEvent('mouseHover', 'Card');
}
function triggerClickAnimation() {
// Emit mouseDown event on 'Button' object
splineApp.current.emitEvent('mouseDown', 'Button');
}
function reverseAnimation() {
// Play animation in reverse
splineApp.current.emitEventReverse('mouseHover', 'Card');
}
return (
<div>
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
onLoad={onLoad}
/>
<button onClick={triggerHoverAnimation}>Hover Effect</button>
<button onClick={triggerClickAnimation}>Click Effect</button>
<button onClick={reverseAnimation}>Reverse</button>
</div>
);
}Available Event Types:
mouseDown- Mouse pressmouseHover- Hover effectmouseUp- Mouse releasekeyDown- Key presskeyUp- Key releasestart- Start eventlookAt- Look at camerafollow- Follow camera
Pattern 5: Next.js Integration with SSR
Use Case: Use Spline in Next.js with server-side rendering benefits
Implementation:
// app/page.js (Next.js 13+ App Router)
import Spline from '@splinetool/react-spline/next';
export default function Home() {
return (
<main>
<div style={{ width: '100vw', height: '100vh' }}>
<Spline scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode" />
</div>
</main>
);
}Benefits:
- Placeholder image shown during SSR
- Faster perceived load times
- Better SEO with fallback content
Pattern 6: Lazy Loading for Performance
Use Case: Defer Spline loading until needed
Implementation:
import React, { Suspense } from 'react';
// Dynamically import Spline
const Spline = React.lazy(() => import('@splinetool/react-spline'));
export default function LazyScene() {
return (
<div>
<h1>My Page Content</h1>
<Suspense fallback={<div>Loading 3D scene...</div>}>
<div style={{ width: '100%', height: '500px' }}>
<Spline scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode" />
</div>
</Suspense>
<p>More content below</p>
</div>
);
}Benefits:
- Reduces initial bundle size
- Improves page load performance
- Shows custom loading UI
Pattern 7: Responsive Spline Scenes
Use Case: Make Spline scenes adapt to different screen sizes
Implementation:
import Spline from '@splinetool/react-spline';
import { useState, useEffect } from 'react';
export default function ResponsiveScene() {
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < 768);
};
checkMobile();
window.addEventListener('resize', checkMobile);
return () => window.removeEventListener('resize', checkMobile);
}, []);
return (
<div style={{
width: '100%',
height: isMobile ? '400px' : '600px'
}}>
<Spline
scene={
isMobile
? "https://prod.spline.design/YOUR-MOBILE-SCENE/scene.splinecode"
: "https://prod.spline.design/YOUR-DESKTOP-SCENE/scene.splinecode"
}
/>
</div>
);
}Alternative Approach (Single Scene):
import Spline from '@splinetool/react-spline';
import { useRef, useEffect } from 'react';
export default function ResponsiveScene() {
const splineApp = useRef();
function onLoad(app) {
splineApp.current = app;
adjustForScreenSize();
}
function adjustForScreenSize() {
if (!splineApp.current) return;
const camera = splineApp.current.findObjectByName('Camera');
const isMobile = window.innerWidth < 768;
if (isMobile) {
// Zoom out on mobile
splineApp.current.setZoom(0.7);
// Adjust camera position
camera.position.z = 1500;
}
}
useEffect(() => {
window.addEventListener('resize', adjustForScreenSize);
return () => window.removeEventListener('resize', adjustForScreenSize);
}, []);
return (
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
onLoad={onLoad}
/>
);
}Integration Patterns
With Three.js (threejs-webgl)
For advanced use cases, combine Spline-designed assets with Three.js code:
1. Export from Spline: Use GLTF/GLB export 2. Import in Three.js: Load using GLTFLoader 3. Enhance with code: Add custom shaders, physics, or effects
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
const loader = new GLTFLoader();
loader.load('spline-model.glb', (gltf) => {
scene.add(gltf.scene);
// Add custom behaviors
});With GSAP (gsap-scrolltrigger)
Trigger Spline animations on scroll:
import { useEffect, useRef } from 'react';
import Spline from '@splinetool/react-spline';
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
export default function ScrollAnimated() {
const splineApp = useRef();
function onLoad(app) {
splineApp.current = app;
ScrollTrigger.create({
trigger: '.scene-container',
start: 'top center',
onEnter: () => {
app.emitEvent('mouseHover', 'Product');
}
});
}
return (
<div className="scene-container">
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
onLoad={onLoad}
/>
</div>
);
}With Framer Motion (motion-framer)
Animate container while Spline handles 3D:
import { motion } from 'framer-motion';
import Spline from '@splinetool/react-spline';
export default function AnimatedContainer() {
return (
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
style={{ width: '100%', height: '600px' }}
>
<Spline scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode" />
</motion.div>
);
}Performance Optimization
1. Enable On-Demand Rendering
Render only when scene changes, not every frame:
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
renderOnDemand={true} // Default is true
/>2. Optimize Scene in Spline Editor
In Spline:
- Reduce polygon count (use decimation)
- Compress textures (lower resolution, use JPG over PNG)
- Limit lights (2-3 lights maximum)
- Use simple materials when possible
- Enable LOD (Level of Detail) for distant objects
3. Lazy Load Heavy Scenes
Use React.lazy() as shown in Pattern 6
4. Preload Critical Scenes
import { useEffect } from 'react';
import Spline from '@splinetool/react-spline';
export default function PreloadedScene() {
useEffect(() => {
// Preload scene assets
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = 'https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode';
document.head.appendChild(link);
}, []);
return (
<Spline scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode" />
);
}5. Mobile Optimizations
- Create separate mobile scenes with lower detail
- Reduce canvas resolution on mobile
- Disable shadows and reflections
- Use simpler materials
<Spline
scene={isMobile ? mobileSceneUrl : desktopSceneUrl}
style={{
width: '100%',
height: isMobile ? '300px' : '600px'
}}
/>Common Pitfalls and Solutions
Pitfall 1: Scene Not Loading
Problem: Spline component renders but scene doesn't appear
Solutions:
// ❌ Wrong: Invalid scene URL
<Spline scene="my-scene.splinecode" />
// ✅ Correct: Full URL from Spline export
<Spline scene="https://prod.spline.design/KFonZGtsoUXP-qx7/scene.splinecode" />
// Check for errors
function onLoad(app) {
console.log('Scene loaded successfully', app);
}
<Spline scene={sceneUrl} onLoad={onLoad} />Also Check:
- Scene is published in Spline editor
- Network tab shows successful file downloads
- No CORS errors in console
Pitfall 2: Object References Lost After Re-render
Problem: Object refs become undefined after component updates
Solution:
// ❌ Wrong: Storing objects without proper refs
let myObject;
function onLoad(spline) {
myObject = spline.findObjectByName('Cube'); // Lost on re-render
}
// ✅ Correct: Use React refs
const myObject = useRef();
function onLoad(spline) {
myObject.current = spline.findObjectByName('Cube');
}Pitfall 3: Performance Issues on Mobile
Problem: Scene runs slowly on mobile devices
Solutions:
// Create mobile-optimized version in Spline editor
// - Fewer polygons (< 50k triangles)
// - Smaller textures (512x512 or less)
// - No shadows or reflections
// - Simpler materials
// Load appropriate version
const isMobile = window.innerWidth < 768;
const sceneUrl = isMobile
? 'https://prod.spline.design/MOBILE-SCENE/scene.splinecode'
: 'https://prod.spline.design/DESKTOP-SCENE/scene.splinecode';
<Spline scene={sceneUrl} renderOnDemand={true} />Pitfall 4: Events Not Firing
Problem: Click or hover events don't trigger
Solutions:
// ❌ Wrong: Using wrong event name
<Spline onMouseDown={handler} /> // Not a Spline prop
// ✅ Correct: Use Spline event props
<Spline onSplineMouseDown={handler} />
// Also ensure object has events in Spline editor:
// 1. Select object in Spline
// 2. Add event in "Events" panel
// 3. Assign state transition or actionPitfall 5: Animation Not Triggering Programmatically
Problem: emitEvent() doesn't trigger animation
Solutions:
// ❌ Wrong: Calling before scene loads
function triggerAnimation() {
splineApp.current.emitEvent('mouseHover', 'Button'); // Error if not loaded
}
// ✅ Correct: Ensure scene is loaded
const [isLoaded, setIsLoaded] = useState(false);
function onLoad(app) {
splineApp.current = app;
setIsLoaded(true);
}
function triggerAnimation() {
if (isLoaded && splineApp.current) {
splineApp.current.emitEvent('mouseHover', 'Button');
}
}
// Also verify in Spline editor:
// - Object has the correct name ('Button')
// - mouseHover event is configured
// - Event has action (state transition, etc.)Pitfall 6: Hydration Errors in Next.js
Problem: Mismatch between server and client render
Solution:
// ❌ Wrong: Using standard import in Next.js
import Spline from '@splinetool/react-spline';
// ✅ Correct: Use Next.js-specific import
import Spline from '@splinetool/react-spline/next';
// Or use dynamic import with ssr: false
import dynamic from 'next/dynamic';
const Spline = dynamic(
() => import('@splinetool/react-spline'),
{ ssr: false }
);Resources
Official Documentation
- Spline Docs: https://docs.spline.design
- React Spline GitHub: https://github.com/splinetool/react-spline
- Spline Community: https://spline.community
Spline Editor
- Web App: https://app.spline.design
- Desktop App: Available for macOS, Windows, Linux
Learning Resources
- Tutorials: https://spline.design/tutorials
- YouTube Channel: Official Spline tutorials
- Examples Gallery: https://spline.design/community
Export Formats
- React component (via
@splinetool/react-spline) - Vanilla JavaScript (Web Code API)
- GLTF/GLB (for Three.js, Babylon.js)
- USDZ (for Apple AR)
- STL (for 3D printing)
- Video/GIF (for marketing)
Related Skills
- threejs-webgl: For code-first 3D development with more control
- react-three-fiber: For building 3D scenes with React and JSX
- babylonjs-engine: Alternative 3D engine with editor workflow
- motion-framer: For animating Spline containers and UI elements
- gsap-scrolltrigger: For scroll-driven Spline animations
- figma-dev-mode: For design-to-code workflow (similar visual approach)
Scripts
This skill includes utility scripts:
project_generator.py- Generate Spline + React starter projectscomponent_builder.py- Build Spline component wrappers with events
Run scripts from the skill directory:
./scripts/project_generator.py
./scripts/component_builder.pyAssets
Starter templates and examples:
starter_spline/- Complete React + Spline templateexamples/- Real-world integration patterns
Spline Interactive - Assets
This directory contains starter templates and examples for Spline + React integration.
Using the Project Generator
The easiest way to get started is using the project_generator.py script:
# Interactive mode
./scripts/project_generator.py
# CLI mode
./scripts/project_generator.py --name my-spline-app
# Next.js project
./scripts/project_generator.py --name my-spline-app --nextjsThis automatically generates a complete project with:
- package.json with all dependencies
- Vite or Next.js configuration
- React components with Spline integration
- Example event handlers
- Starter CSS
- README with setup instructions
Project Structure
Generated projects have this structure:
my-spline-app/
├── package.json
├── vite.config.js (or next.config.js)
├── index.html (Vite only)
├── README.md
├── src/
│ ├── main.jsx (Vite only)
│ ├── App.jsx
│ └── App.css
└── app/ (Next.js only)
└── page.jsxComponent Templates
Use component_builder.py to generate reusable Spline components:
# Interactive mode
./scripts/component_builder.py
# Generate specific component
./scripts/component_builder.py --name ProductViewer --type interactive
# Save to file
./scripts/component_builder.py --name ProductViewer --type interactive --output src/components/ProductViewer.jsxAvailable Component Types
1. basic - Simple Spline scene wrapper 2. interactive - With click/hover event handling 3. animated - With animation trigger methods 4. controlled - Exposes methods via ref for parent control 5. responsive - Adapts to mobile/desktop screen sizes 6. lazy - Lazy-loaded with React.lazy()
Example Integrations
With GSAP ScrollTrigger
import { useRef, useEffect } from 'react';
import Spline from '@splinetool/react-spline';
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
export default function ScrollSpline() {
const splineApp = useRef();
function onLoad(app) {
splineApp.current = app;
ScrollTrigger.create({
trigger: '.scene',
start: 'top center',
onEnter: () => app.emitEvent('mouseHover', 'Product')
});
}
return (
<div className="scene">
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
onLoad={onLoad}
/>
</div>
);
}With Framer Motion
import { motion } from 'framer-motion';
import Spline from '@splinetool/react-spline';
export default function AnimatedSpline() {
return (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8 }}
style={{ width: '100%', height: '600px' }}
>
<Spline scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode" />
</motion.div>
);
}Product Configurator
import { useRef, useState } from 'react';
import Spline from '@splinetool/react-spline';
export default function ProductConfigurator() {
const product = useRef();
const splineApp = useRef();
const [color, setColor] = useState('#ff6b6b');
function onLoad(spline) {
splineApp.current = spline;
product.current = spline.findObjectByName('Product');
}
function changeColor(newColor) {
setColor(newColor);
if (product.current) {
product.current.material.color.set(parseInt(newColor.substring(1), 16));
}
}
function rotate() {
if (product.current) {
product.current.rotation.y += Math.PI / 4;
}
}
return (
<div>
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
onLoad={onLoad}
/>
<div className="controls">
<button onClick={() => changeColor('#ff6b6b')}>Red</button>
<button onClick={() => changeColor('#4ecdc4')}>Cyan</button>
<button onClick={() => changeColor('#ffe66d')}>Yellow</button>
<button onClick={rotate}>Rotate</button>
</div>
</div>
);
}Multi-Scene Viewer
import { useState } from 'react';
import Spline from '@splinetool/react-spline';
const scenes = {
scene1: 'https://prod.spline.design/SCENE-1/scene.splinecode',
scene2: 'https://prod.spline.design/SCENE-2/scene.splinecode',
scene3: 'https://prod.spline.design/SCENE-3/scene.splinecode'
};
export default function SceneSwitcher() {
const [currentScene, setCurrentScene] = useState('scene1');
return (
<div>
<Spline scene={scenes[currentScene]} />
<div className="tabs">
<button onClick={() => setCurrentScene('scene1')}>Scene 1</button>
<button onClick={() => setCurrentScene('scene2')}>Scene 2</button>
<button onClick={() => setCurrentScene('scene3')}>Scene 3</button>
</div>
</div>
);
}Getting Your Spline Scene URL
1. Open your scene in Spline editor (https://app.spline.design) 2. Click the "Export" button (top right) 3. Select "Code Export" → "React" 4. Copy the scene URL (format: https://prod.spline.design/[ID]/scene.splinecode) 5. Replace YOUR-SCENE-ID in the templates with your actual scene URL
Common Use Cases
Marketing Hero Section
import Spline from '@splinetool/react-spline';
import './Hero.css';
export default function Hero() {
return (
<section className="hero">
<div className="hero-content">
<h1>Welcome to Our Product</h1>
<p>Experience 3D interaction</p>
<button>Get Started</button>
</div>
<div className="hero-scene">
<Spline scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode" />
</div>
</section>
);
}Interactive Portfolio
import { useRef } from 'react';
import Spline from '@splinetool/react-spline';
export default function Portfolio() {
const splineApp = useRef();
function showProject(projectName) {
splineApp.current?.emitEvent('mouseHover', projectName);
}
return (
<div className="portfolio">
<nav>
<button onClick={() => showProject('Project1')}>Project 1</button>
<button onClick={() => showProject('Project2')}>Project 2</button>
<button onClick={() => showProject('Project3')}>Project 3</button>
</nav>
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
onLoad={(app) => (splineApp.current = app)}
/>
</div>
);
}Performance Tips
1. Use On-Demand Rendering (enabled by default):
<Spline scene={sceneUrl} renderOnDemand={true} />2. Lazy Load Heavy Scenes:
const Spline = React.lazy(() => import('@splinetool/react-spline'));3. Optimize in Spline Editor:
- Reduce polygon count
- Compress textures
- Limit lights to 2-3
- Use simple materials
4. Create Mobile Versions:
- Lower detail meshes
- Smaller textures (512x512)
- No shadows/reflections
Troubleshooting
Scene Not Loading
- Verify scene URL is complete and correct
- Check scene is published in Spline editor
- Look for CORS errors in browser console
- Ensure both
@splinetool/react-splineand@splinetool/runtimeare installed
Events Not Working
- Use
onSplineMouseDownnotonMouseDown - Verify objects have events configured in Spline editor
- Check object names match exactly (case-sensitive)
Performance Issues
- Enable renderOnDemand
- Create mobile-specific scenes with lower detail
- Use lazy loading for heavy scenes
- Profile with Chrome DevTools
Additional Resources
Spline React API Reference
Complete reference for @splinetool/react-spline and Spline Application API.
Installation
npm install @splinetool/react-spline @splinetool/runtime
# or
yarn add @splinetool/react-spline @splinetool/runtimeSpline Component
Import
// Standard React
import Spline from '@splinetool/react-spline';
// Next.js (with SSR support)
import Spline from '@splinetool/react-spline/next';Props
scene (required)
- Type:
string - Description: URL to the Spline scene file
- Example:
"https://prod.spline.design/6Wq1Q7YGyM-iab9i/scene.splinecode"
onLoad
- Type:
(spline: Application) => void - Description: Called once the scene has loaded. Provides Spline Application instance
- Example:
function onLoad(spline) {
console.log('Scene loaded', spline);
const obj = spline.findObjectByName('Cube');
}
<Spline scene={sceneUrl} onLoad={onLoad} />renderOnDemand
- Type:
boolean - Default:
true - Description: Enable on-demand rendering (only renders when scene changes)
- Example:
<Spline scene={sceneUrl} renderOnDemand={false} />
className
- Type:
string - Description: CSS class name(s) for the canvas container
- Example:
<Spline scene={sceneUrl} className="my-scene" />
style
- Type:
React.CSSProperties - Description: Inline CSS styles for the canvas container
- Example:
<Spline scene={sceneUrl} style={{ width: '100%', height: '600px' }} />
id
- Type:
string - Description: HTML id attribute for the canvas element
- Example:
<Spline scene={sceneUrl} id="main-scene" />
ref
- Type:
React.Ref<HTMLDivElement> - Description: React ref pointing to the container div element
- Example:
const containerRef = useRef();
<Spline scene={sceneUrl} ref={containerRef} />Event Handler Props
onSplineMouseDown
- Type:
(e: SplineEvent) => void - Description: Fired when mouse button is pressed on an object
- Event Object:
e.target- The object that was clicked (contains name, id, position, rotation, scale)e.target.name- Object name from Spline editore.target.id- Object UUID- Example:
function onSplineMouseDown(e) {
console.log('Clicked:', e.target.name);
console.log('Position:', e.target.position);
}onSplineMouseUp
- Type:
(e: SplineEvent) => void - Description: Fired when mouse button is released
- Example:
function onSplineMouseUp(e) {
console.log('Released:', e.target.name);
}onSplineMouseHover
- Type:
(e: SplineEvent) => void - Description: Fired when mouse hovers over an object
- Example:
function onSplineMouseHover(e) {
console.log('Hovering:', e.target.name);
}onSplineKeyDown
- Type:
(e: SplineEvent) => void - Description: Fired when a keyboard key is pressed
- Example:
function onSplineKeyDown(e) {
console.log('Key pressed:', e.key);
}onSplineKeyUp
- Type:
(e: SplineEvent) => void - Description: Fired when a keyboard key is released
onSplineStart
- Type:
(e: SplineEvent) => void - Description: Fired when the scene starts (after loading)
onSplineLookAt
- Type:
(e: SplineEvent) => void - Description: Fired when a camera look-at event occurs
onSplineFollow
- Type:
(e: SplineEvent) => void - Description: Fired when a camera follow event occurs
onSplineScroll
- Type:
(e: SplineEvent) => void - Description: Fired when a scroll event occurs in the scene
Spline Application API
The Application object is provided via the onLoad callback.
Methods
emitEvent(eventName, nameOrUuid)
- Description: Triggers a Spline event on an object
- Parameters:
eventName(SplineEventName) - The event to triggernameOrUuid(string) - Object name or UUID- Returns:
void - Example:
spline.emitEvent('mouseHover', 'Button');
spline.emitEvent('mouseDown', '8E8C2DDD-18B6-4C54-861D-7ED2519DE20E');emitEventReverse(eventName, nameOrUuid)
- Description: Triggers a Spline event in reverse (from last state to first)
- Parameters:
eventName(SplineEventName) - The event to triggernameOrUuid(string) - Object name or UUID- Returns:
void - Example:
spline.emitEventReverse('mouseHover', 'Card');findObjectById(uuid)
- Description: Finds an object by its UUID
- Parameters:
uuid(string) - Object UUID from Spline editor- Returns:
SPEObject- The object if found, undefined otherwise - Example:
const obj = spline.findObjectById('8E8C2DDD-18B6-4C54-861D-7ED2519DE20E');
console.log(obj.name, obj.position);findObjectByName(name)
- Description: Finds the first object matching the specified name
- Parameters:
name(string) - Object name from Spline editor- Returns:
SPEObject- The object if found, undefined otherwise - Example:
const cube = spline.findObjectByName('Cube');
cube.position.x += 10;setZoom(zoom)
- Description: Sets the camera zoom level
- Parameters:
zoom(number) - Zoom value (1.0 is default, <1.0 zooms out, >1.0 zooms in)- Returns:
void - Example:
spline.setZoom(1.5); // Zoom in
spline.setZoom(0.7); // Zoom outSplineEventName Type
Available event types for emitEvent() and emitEventReverse():
'mouseDown'- Mouse button pressed'mouseHover'- Mouse hover'mouseUp'- Mouse button released'keyDown'- Keyboard key pressed'keyUp'- Keyboard key released'start'- Scene start event'lookAt'- Camera look-at event'follow'- Camera follow event
SPEObject Interface
Objects returned by findObjectById() and findObjectByName() have the following properties:
Properties
name
- Type:
string - Description: Object name from Spline editor
- Example:
'Cube','Button','Product'
id
- Type:
string - Description: Object UUID
- Example:
'8E8C2DDD-18B6-4C54-861D-7ED2519DE20E'
position
- Type:
{ x: number, y: number, z: number } - Description: Object position in 3D space
- Mutable: Yes
- Example:
console.log(obj.position); // { x: 0, y: 100, z: 50 }
obj.position.x += 10;
obj.position.y = 200;rotation
- Type:
{ x: number, y: number, z: number } - Description: Object rotation in radians
- Mutable: Yes
- Example:
obj.rotation.y += Math.PI / 4; // Rotate 45 degrees
obj.rotation.x = Math.PI / 2; // Rotate 90 degreesscale
- Type:
{ x: number, y: number, z: number } - Description: Object scale multiplier
- Mutable: Yes
- Example:
obj.scale.x = 2; // Scale to 2x width
obj.scale.y = 0.5; // Scale to half height
obj.scale.z = 1.5;material
- Type:
Material - Description: Object material properties
- Properties:
color- Material color (use.set()method)- Example:
// Set color using hex value
obj.material.color.set(0xff6b6b); // Red
obj.material.color.set(0x4ecdc4); // Cyan
obj.material.color.set(0xffe66d); // Yellowvisible
- Type:
boolean - Description: Object visibility
- Mutable: Yes
- Example:
obj.visible = false; // Hide object
obj.visible = true; // Show objectMethods
emitEvent(eventName)
- Description: Triggers an event on this specific object
- Parameters:
eventName(SplineEventName) - The event to trigger- Returns:
void - Example:
const button = spline.findObjectByName('Button');
button.emitEvent('mouseHover'); // Trigger hover animationSplineEvent Interface
Event object passed to event handler callbacks:
Properties
target
- Type:
SPEObject - Description: The object that triggered the event
- Example:
function onSplineMouseDown(e) {
console.log('Clicked object:', e.target.name);
console.log('Object ID:', e.target.id);
console.log('Position:', e.target.position);
}type
- Type:
string - Description: Event type name
- Values:
'mouseDown','mouseUp','mouseHover', etc.
Usage Examples
Complete Component Example
import { useRef, useState } from 'react';
import Spline from '@splinetool/react-spline';
export default function InteractiveScene() {
const splineApp = useRef();
const [isLoaded, setIsLoaded] = useState(false);
function onLoad(spline) {
splineApp.current = spline;
setIsLoaded(true);
console.log('Scene loaded');
}
function onSplineMouseDown(e) {
console.log('Clicked:', e.target.name);
if (e.target.name === 'Button') {
// Trigger animation
splineApp.current.emitEvent('mouseHover', 'Card');
}
}
function rotateAllObjects() {
if (!isLoaded) return;
const cube = splineApp.current.findObjectByName('Cube');
const sphere = splineApp.current.findObjectByName('Sphere');
if (cube) cube.rotation.y += Math.PI / 4;
if (sphere) sphere.rotation.x += Math.PI / 4;
}
return (
<div>
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
onLoad={onLoad}
onSplineMouseDown={onSplineMouseDown}
style={{ width: '100%', height: '600px' }}
/>
{isLoaded && (
<button onClick={rotateAllObjects}>
Rotate Objects
</button>
)}
</div>
);
}Next.js Example
// app/components/SplineScene.jsx
'use client';
import Spline from '@splinetool/react-spline/next';
export default function SplineScene() {
return (
<div style={{ width: '100vw', height: '100vh' }}>
<Spline scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode" />
</div>
);
}Lazy Loading Example
import React, { Suspense } from 'react';
const Spline = React.lazy(() => import('@splinetool/react-spline'));
export default function LazyScene() {
return (
<Suspense fallback={<div>Loading 3D scene...</div>}>
<Spline scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode" />
</Suspense>
);
}Color Reference
Common hex color values for material.color.set():
// Red tones
0xff0000 // Pure red
0xff6b6b // Coral red
0xe74c3c // Soft red
// Blue tones
0x0000ff // Pure blue
0x3498db // Sky blue
0x2c3e50 // Dark blue
// Green tones
0x00ff00 // Pure green
0x2ecc71 // Emerald green
0x27ae60 // Dark green
// Yellow/Orange
0xffff00 // Pure yellow
0xffe66d // Soft yellow
0xf39c12 // Orange
// Purple/Pink
0x9b59b6 // Purple
0xe91e63 // Pink
0xff69b4 // Hot pink
// Cyan/Teal
0x00ffff // Pure cyan
0x4ecdc4 // Teal
0x1abc9c // Turquoise
// Grayscale
0xffffff // White
0xcccccc // Light gray
0x95a5a6 // Gray
0x7f8c8d // Dark gray
0x000000 // BlackMath Helpers
Common rotation values (radians):
Math.PI / 6 // 30 degrees
Math.PI / 4 // 45 degrees
Math.PI / 3 // 60 degrees
Math.PI / 2 // 90 degrees
Math.PI // 180 degrees
Math.PI * 2 // 360 degrees
// Convert degrees to radians
const radians = (degrees * Math.PI) / 180;
// Convert radians to degrees
const degrees = (radians * 180) / Math.PI;Troubleshooting
Scene Not Loading
- Verify scene URL is correct and complete
- Check that scene is published in Spline editor
- Look for CORS errors in console
- Ensure
@splinetool/runtimeis installed
Events Not Firing
- Use
onSplineMouseDownnotonMouseDown - Verify object has events configured in Spline editor
- Check object name matches exactly
Object Not Found
- Verify object name in Spline editor matches exactly (case-sensitive)
- Check that
onLoadhas been called before finding objects - Use
findObjectByIdif name changes frequently
Performance Issues
- Enable
renderOnDemand={true} - Reduce polygon count in Spline editor
- Compress textures
- Create mobile-specific scenes
- Use lazy loading for heavy scenes
TypeScript Support
import { Application, SPEObject, SplineEvent } from '@splinetool/runtime';
import Spline from '@splinetool/react-spline';
interface Props {
sceneUrl: string;
}
export default function MyScene({ sceneUrl }: Props) {
const splineApp = useRef<Application | null>(null);
function onLoad(app: Application): void {
splineApp.current = app;
}
function onSplineMouseDown(e: SplineEvent): void {
const target: SPEObject = e.target;
console.log(target.name);
}
return (
<Spline
scene={sceneUrl}
onLoad={onLoad}
onSplineMouseDown={onSplineMouseDown}
/>
);
}#!/usr/bin/env python3
"""
Spline Component Wrapper Generator
Generates reusable Spline component wrappers with event handling.
Usage:
./component_builder.py # Interactive mode
./component_builder.py --name ProductViewer # CLI mode
"""
import argparse
import sys
from pathlib import Path
COMPONENT_TEMPLATES = {
'basic': '''import Spline from '@splinetool/react-spline';
export default function {name}({{ sceneUrl }}) {{
return (
<div style={{{{ width: '100%', height: '600px' }}}}>
<Spline scene={{sceneUrl}} />
</div>
);
}}
''',
'interactive': '''import {{ useRef, useState }} from 'react';
import Spline from '@splinetool/react-spline';
export default function {name}({{ sceneUrl, onObjectClick }}) {{
const splineApp = useRef();
const [isLoaded, setIsLoaded] = useState(false);
function onLoad(spline) {{
splineApp.current = spline;
setIsLoaded(true);
}}
function onSplineMouseDown(e) {{
console.log('Clicked:', e.target.name);
if (onObjectClick) {{
onObjectClick(e.target);
}}
}}
return (
<Spline
scene={{sceneUrl}}
onLoad={{onLoad}}
onSplineMouseDown={{onSplineMouseDown}}
/>
);
}}
''',
'animated': '''import {{ useRef }} from 'react';
import Spline from '@splinetool/react-spline';
export default function {name}({{ sceneUrl, animationTrigger }}) {{
const splineApp = useRef();
function onLoad(spline) {{
splineApp.current = spline;
}}
// Expose animation controls
const triggerAnimation = (objectName, eventType = 'mouseHover') => {{
if (splineApp.current) {{
splineApp.current.emitEvent(eventType, objectName);
}}
}};
const reverseAnimation = (objectName, eventType = 'mouseHover') => {{
if (splineApp.current) {{
splineApp.current.emitEventReverse(eventType, objectName);
}}
}};
return (
<Spline
scene={{sceneUrl}}
onLoad={{onLoad}}
/>
);
}}
''',
'controlled': '''import {{ useRef, useImperativeHandle, forwardRef }} from 'react';
import Spline from '@splinetool/react-spline';
const {name} = forwardRef(({{ sceneUrl }}, ref) => {{
const splineApp = useRef();
function onLoad(spline) {{
splineApp.current = spline;
}}
// Expose methods to parent component
useImperativeHandle(ref, () => ({{
findObject: (name) => {{
return splineApp.current?.findObjectByName(name);
}},
emitEvent: (eventName, objectName) => {{
splineApp.current?.emitEvent(eventName, objectName);
}},
setZoom: (zoom) => {{
splineApp.current?.setZoom(zoom);
}},
getApp: () => splineApp.current
}}));
return (
<Spline
scene={{sceneUrl}}
onLoad={{onLoad}}
/>
);
}});
export default {name};
''',
'responsive': '''import {{ useRef, useEffect, useState }} from 'react';
import Spline from '@splinetool/react-spline';
export default function {name}({{ mobileSceneUrl, desktopSceneUrl }}) {{
const splineApp = useRef();
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {{
const checkMobile = () => {{
setIsMobile(window.innerWidth < 768);
}};
checkMobile();
window.addEventListener('resize', checkMobile);
return () => window.removeEventListener('resize', checkMobile);
}}, []);
function onLoad(spline) {{
splineApp.current = spline;
// Adjust for mobile
if (isMobile) {{
spline.setZoom(0.7);
}}
}}
const sceneUrl = isMobile ? mobileSceneUrl : desktopSceneUrl;
return (
<div style={{{{
width: '100%',
height: isMobile ? '400px' : '600px'
}}}}>
<Spline
scene={{sceneUrl}}
onLoad={{onLoad}}
/>
</div>
);
}}
''',
'lazy': '''import React, {{ Suspense }} from 'react';
const Spline = React.lazy(() => import('@splinetool/react-spline'));
export default function {name}({{ sceneUrl, fallback }}) {{
return (
<Suspense fallback={{fallback || <div>Loading 3D scene...</div>}}>
<div style={{{{ width: '100%', height: '600px' }}}}>
<Spline scene={{sceneUrl}} />
</div>
</Suspense>
);
}}
'''
}
def generate_component(name, component_type, output_path=None):
"""Generate component code"""
if component_type not in COMPONENT_TEMPLATES:
print(f"❌ Unknown component type: {component_type}")
print(f"Available types: {', '.join(COMPONENT_TEMPLATES.keys())}")
sys.exit(1)
template = COMPONENT_TEMPLATES[component_type]
code = template.format(name=name)
# Output to file or stdout
if output_path:
output_file = Path(output_path)
output_file.parent.mkdir(parents=True, exist_ok=True)
with open(output_file, 'w') as f:
f.write(code)
print(f"✅ Component created: {output_file}")
else:
print("\n" + "=" * 60)
print(f"// {name}.jsx")
print("=" * 60)
print(code)
print("=" * 60)
def main():
parser = argparse.ArgumentParser(
description='Generate Spline component wrappers'
)
parser.add_argument(
'--name',
type=str,
help='Component name (e.g., ProductViewer)'
)
parser.add_argument(
'--type',
type=str,
choices=list(COMPONENT_TEMPLATES.keys()),
help='Component type'
)
parser.add_argument(
'--output',
type=str,
help='Output file path (default: print to stdout)'
)
args = parser.parse_args()
# Interactive mode
if not args.name or not args.type:
print("🎨 Spline Component Builder\n")
if not args.name:
name = input("Component name (e.g., ProductViewer): ").strip()
if not name:
print("❌ Component name is required")
sys.exit(1)
else:
name = args.name
if not args.type:
print("\nComponent types:")
for i, comp_type in enumerate(COMPONENT_TEMPLATES.keys(), 1):
print(f" {i}. {comp_type}")
choice = input("\nSelect type (1-6): ").strip()
try:
comp_type = list(COMPONENT_TEMPLATES.keys())[int(choice) - 1]
except (ValueError, IndexError):
print("❌ Invalid choice")
sys.exit(1)
else:
comp_type = args.type
output_path = args.output
else:
name = args.name
comp_type = args.type
output_path = args.output
# Generate component
generate_component(name, comp_type, output_path)
if __name__ == '__main__':
main()
#!/usr/bin/env python3
"""
Spline + React Project Generator
Generates boilerplate React projects with Spline integration.
Usage:
./project_generator.py # Interactive mode
./project_generator.py --name my-project # CLI mode
"""
import argparse
import json
import os
import sys
from pathlib import Path
def create_project_structure(project_name, include_nextjs=False):
"""Create complete project structure with all necessary files"""
base_path = Path(project_name)
# Create directories
directories = [
base_path,
base_path / 'src',
base_path / 'public',
]
for directory in directories:
directory.mkdir(parents=True, exist_ok=True)
# package.json
package_json = {
"name": project_name,
"version": "0.1.0",
"private": True,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@splinetool/react-spline": "^2.2.6",
"@splinetool/runtime": "^0.9.508"
},
"devDependencies": {
"@vitejs/plugin-react": "^4.0.0",
"vite": "^4.3.9"
},
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
if include_nextjs:
package_json["dependencies"]["next"] = "^14.0.0"
package_json["scripts"] = {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
with open(base_path / 'package.json', 'w') as f:
json.dump(package_json, f, indent=2)
# vite.config.js (if not Next.js)
if not include_nextjs:
vite_config = """import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
port: 3000
}
});
"""
with open(base_path / 'vite.config.js', 'w') as f:
f.write(vite_config)
# index.html (if not Next.js)
if not include_nextjs:
index_html = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Spline React App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
"""
with open(base_path / 'index.html', 'w') as f:
f.write(index_html)
# src/main.jsx (if not Next.js)
if not include_nextjs:
main_jsx = """import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './App.css';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
"""
with open(base_path / 'src' / 'main.jsx', 'w') as f:
f.write(main_jsx)
# src/App.jsx or app/page.jsx
if include_nextjs:
app_dir = base_path / 'app'
app_dir.mkdir(exist_ok=True)
page_jsx = """'use client';
import Spline from '@splinetool/react-spline/next';
import { useRef } from 'react';
export default function Home() {
const splineApp = useRef();
function onLoad(spline) {
splineApp.current = spline;
console.log('Spline scene loaded');
}
function onSplineMouseDown(e) {
console.log('Clicked:', e.target.name);
}
return (
<main style={{ width: '100vw', height: '100vh' }}>
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
onLoad={onLoad}
onSplineMouseDown={onSplineMouseDown}
/>
</main>
);
}
"""
with open(app_dir / 'page.jsx', 'w') as f:
f.write(page_jsx)
else:
app_jsx = """import { useRef, useState } from 'react';
import Spline from '@splinetool/react-spline';
export default function App() {
const splineApp = useRef();
const [isLoaded, setIsLoaded] = useState(false);
function onLoad(spline) {
splineApp.current = spline;
setIsLoaded(true);
console.log('Spline scene loaded');
}
function onSplineMouseDown(e) {
console.log('Clicked:', e.target.name);
if (e.target.name === 'Button') {
splineApp.current.emitEvent('mouseHover', 'Card');
}
}
return (
<div className="app">
<div className="scene-container">
<Spline
scene="https://prod.spline.design/YOUR-SCENE-ID/scene.splinecode"
onLoad={onLoad}
onSplineMouseDown={onSplineMouseDown}
/>
</div>
{isLoaded && (
<div className="controls">
<button onClick={() => splineApp.current.setZoom(1.5)}>
Zoom In
</button>
<button onClick={() => splineApp.current.setZoom(0.7)}>
Zoom Out
</button>
</div>
)}
</div>
);
}
"""
with open(base_path / 'src' / 'App.jsx', 'w') as f:
f.write(app_jsx)
# CSS
if not include_nextjs:
app_css = """* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.app {
width: 100vw;
height: 100vh;
position: relative;
}
.scene-container {
width: 100%;
height: 100%;
}
.controls {
position: absolute;
top: 20px;
left: 20px;
display: flex;
gap: 10px;
z-index: 10;
}
button {
padding: 10px 20px;
background: rgba(255, 255, 255, 0.9);
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
button:hover {
background: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
button:active {
transform: scale(0.98);
}
"""
with open(base_path / 'src' / 'App.css', 'w') as f:
f.write(app_css)
# README.md
readme = f"""# {project_name}
Spline + React project
## Setup
```bash
npm install
```
## Development
```bash
npm run dev
```
Visit http://localhost:3000
## Update Scene URL
Replace `YOUR-SCENE-ID` in the code with your actual Spline scene ID.
Get your scene URL from Spline:
1. Open your scene in Spline editor
2. Click "Export" button
3. Select "Code Export" > "React"
4. Copy the scene URL
## Build
```bash
npm run build
```
## Learn More
- [Spline Documentation](https://docs.spline.design)
- [React Spline GitHub](https://github.com/splinetool/react-spline)
"""
with open(base_path / 'README.md', 'w') as f:
f.write(readme)
print(f"\n✅ Project '{project_name}' created successfully!")
print(f"\nNext steps:")
print(f" cd {project_name}")
print(f" npm install")
print(f" npm run dev")
print(f"\n📝 Don't forget to update the Spline scene URL!")
def main():
parser = argparse.ArgumentParser(
description='Generate Spline + React project boilerplate'
)
parser.add_argument(
'--name',
type=str,
help='Project name'
)
parser.add_argument(
'--nextjs',
action='store_true',
help='Use Next.js instead of Vite'
)
args = parser.parse_args()
# Interactive mode
if not args.name:
print("🚀 Spline + React Project Generator\n")
project_name = input("Project name: ").strip()
if not project_name:
print("❌ Project name is required")
sys.exit(1)
use_nextjs = input("Use Next.js? (y/N): ").strip().lower() == 'y'
else:
project_name = args.name
use_nextjs = args.nextjs
# Create project
create_project_structure(project_name, use_nextjs)
if __name__ == '__main__':
main()
Related skills
How it compares
Use spline-interactive for Spline-plus-React scaffolding; use generic React skills when 3D is not required.
FAQ
What does spline-interactive do?
Browser-based 3D design tool with visual editor, animation, and web export. Use this skill when creating 3D scenes without code, designing interactive web experiences, prototyping 3D UI, exporting to
When should I use spline-interactive?
During build frontend work for frontend development.
Is spline-interactive safe to install?
Review the Security Audits panel on this listing before production use.