Enterprise-Grade 3D Framework Design by Kevin Parra Lopez
Demonstrating Creative Tech Lead expertise in scalable immersive experiences
This project showcases advanced Creative Technology Leadership through a sophisticated architecture that combines:
- Clean Architecture Principles - Separation of concerns for maintainability
- SOLID Design Patterns - Professional OOP practices
- Performance-First Engineering - Optimized for 60fps+ real-time rendering
- Scalable Component Design - Enterprise-ready modular structure
This repository is a modernized GPU particles lab with an explicit goal of reaching visual parity with a legacy reference while adopting a maintainable, scalable stack.
- Entry:
src/ui/components/R3FCanva.tsxcreates the<Canvas>, sets fog/clear color, and mountsModernCore. - Scene:
src/ui/scenes/ModernCore.tsxcomposes lights, floor,ControlsPanel(Leva), andAdaptiveParticles. - PostFX:
PostProcessingFxmounts the legacy‑compatible postprocessing pipeline (FXAA, Bloom, Motion Blur) undersrc/assets/postprocessing/**.
- Component:
src/ui/components/particles/FboParticles.tsx. - Simulation: offscreen ping‑pong FBO with
OrthographicCamera(-1..1)and a fullscreen quad. Shaders live insrc/glsl/glsl3/simulationShaders.ts. - Initialization: GLSL3 seeding of a Fibonacci sphere + life into a default texture.
- Uniforms (sim):
resolution,texturePosition,textureDefaultPosition,time,speed,dieSpeed,radius,curlSize,attraction,initAnimation,mouse3d,followMouse. - Draw: points or triangles. Point path samples the sim texture in the vertex shader; triangle path morphs orientations via
flipRatiowith attributesposition,positionFlip, andfboUV. - Motion blur: particles attach a
motionMaterialexposingtexturePosition,texturePrevPosition, andu_prevModelViewMatrixfor the Motion Blur effect’s velocity pass.
- Component:
src/ui/components/particles/CpuParticles.tsx. - Behavior: generates sphere positions once and attracts them to a z=0 projected mouse point.
- Selection:
AdaptiveParticleschooses CPU when WebGL2 is missing/risky (seeutils/capabilities.ts).
- Composer/effects under
src/assets/postprocessing/**. - FXAA: GLSL3 self‑contained shader (no glslify) to avoid pragma/precision compile issues.
- Bloom: legacy‑accurate 2‑pass separable blur with threshold/smoothing/weights parity.
- Motion Blur: velocity pass (reading per‑object motion material), then lines/sampling pass with quality scaling.
- Safety: the composer avoids feedback loops when a pass samples from the target it renders to.
- Zustand store mirrors legacy toggles: amount (cols/rows/radius), speed, dieSpeed, curlSize, attraction, followMouse, triangle size/flip, and post‑fx toggles/quality.
- Leva UI updates values live; amount changes trigger a page reload to resize buffers safely.
// Creative Tech Lead Decision Matrix
interface TechDecision {
performance: "critical" | "important" | "nice-to-have";
scalability: "enterprise" | "team" | "personal";
maintainability: "long-term" | "medium-term" | "short-term";
innovation: "cutting-edge" | "proven" | "stable";
}🎨 PRESENTATION LAYER
├── React Three Fiber Components # Declarative 3D rendering
├── Custom Hooks for 3D Logic # Reusable stateful behavior
├── Styled Components + Themes # Design system consistency
└── Performance Monitoring UI # Real-time metrics display
🧠 APPLICATION LAYER
├── Zustand State Management # Predictable state updates
├── Use Case Orchestration # Business logic coordination
├── Event System # Decoupled component communication
└── Performance Analytics # Data-driven optimization
💎 DOMAIN LAYER
├── Particle System Entities # Core business objects
├── Physics & Animation Services # Computational logic
├── Scene Management # 3D world coordination
└── Repository Interfaces # Data access contracts
🔌 INFRASTRUCTURE LAYER
├── Three.js WebGL Integration # Low-level 3D rendering
├── Shader & Material Systems # GPU programming
├── Asset Loading & Caching # Resource management
└── External API Adapters # Third-party integrations
│ │ ├── ParticleService.ts # Lógica de partículas │ │ ├── PhysicsService.ts # Lógica de física │ │ └── AnimationService.ts # Lógica de animaciones │ └── repositories/ # Interfaces de repositorios │ └── ISceneRepository.ts │ ├── infrastructure/ # Infrastructure Layer │ ├── three/ # Implementaciones Three.js │ │ ├── loaders/ # Carga de assets 3D │ │ ├── materials/ # Materiales y shaders │ │ ├── geometries/ # Geometrías custom │ │ └── renderers/ # Configuraciones de renderer │ ├── repositories/ # Implementaciones de repositorios │ └── external/ # APIs externas, archivos │ ├── presentation/ # UI Layer (React) │ ├── components/ # Componentes React │ │ ├── ui/ # Componentes UI puros │ │ ├── experience/ # Componentes de experiencia 3D │ │ │ ├── Scene.tsx # Componente escena principal │ │ │ ├── Camera.tsx # Componente cámara │ │ │ └── Effects.tsx # Post-processing effects │ │ └── particles/ # Componentes específicos de partículas │ ├── hooks/ # Custom hooks │ │ ├── useParticles.ts # Hook para partículas │ │ ├── useScene3D.ts # Hook para escena │ │ └── usePhysics.ts # Hook para física │ └── layouts/ # Layouts de la aplicación │ ├── shared/ # Código compartido │ ├── constants/ # Constantes del proyecto │ ├── types/ # Tipos TypeScript │ ├── utils/ # Utilidades puras │ └── config/ # Configuraciones │ └── assets/ # Recursos estáticos ├── models/ # Modelos 3D (.gltf, .fbx) ├── textures/ # Texturas ├── shaders/ # Shaders GLSL └── audio/ # Archivos de audio
## Patrones de Diseño Implementados
### 1. **Repository Pattern**
```typescript
// domain/repositories/ISceneRepository.ts
interface ISceneRepository {
loadScene(id: string): Promise<Scene3D>;
saveScene(scene: Scene3D): Promise<void>;
getSceneMetadata(id: string): Promise<SceneMetadata>;
}
// domain/factories/ParticleSystemFactory.ts
export class ParticleSystemFactory {
static createFireworks(): ParticleSystem {
return new ParticleSystem({
type: "fireworks",
count: 1000,
behavior: new FireworksBehavior(),
});
}
}// domain/services/ParticleService.ts
export class ParticleService {
private observers: ParticleObserver[] = [];
subscribe(observer: ParticleObserver): void {
this.observers.push(observer);
}
notify(event: ParticleEvent): void {
this.observers.forEach((o) => o.update(event));
}
}// domain/strategies/AnimationStrategy.ts
interface AnimationStrategy {
animate(target: Object3D, delta: number): void;
}
export class OrbitAnimationStrategy implements AnimationStrategy {
animate(target: Object3D, delta: number): void {
target.rotation.y += delta * 0.5;
}
}// app/stores/scene.store.ts
interface SceneState {
// Estado
currentScene: Scene3D | null;
isLoading: boolean;
// Acciones
loadScene: (id: string) => Promise<void>;
updateScene: (updates: Partial<Scene3D>) => void;
resetScene: () => void;
}
export const useSceneStore = create<SceneState>((set, get) => ({
currentScene: null,
isLoading: false,
loadScene: async (id: string) => {
set({ isLoading: true });
const scene = await sceneRepository.loadScene(id);
set({ currentScene: scene, isLoading: false });
},
updateScene: (updates) => {
const current = get().currentScene;
if (current) {
set({ currentScene: { ...current, ...updates } });
}
},
resetScene: () => set({ currentScene: null }),
}));// presentation/components/experience/Scene.tsx
export const Scene: FC<SceneProps> = ({ children, ...props }) => {
const { scene, camera } = useScene3D()
return (
<Canvas {...props}>
<Suspense fallback={<Loader />}>
<Environment preset="city" />
<CameraController camera={camera} />
<EffectsComposer>
<PostProcessingEffects />
</EffectsComposer>
{children}
</Suspense>
</Canvas>
)
}// presentation/hooks/useParticles.ts
export const useParticles = (config: ParticleConfig) => {
const particleService = useMemo(() => new ParticleService(config), [config]);
const [particles, setParticles] = useState<Particle[]>([]);
useFrame((_, delta) => {
const updated = particleService.update(delta);
setParticles(updated);
});
return { particles, particleService };
};// infrastructure/three/InstancedParticles.ts
export class InstancedParticles extends InstancedMesh {
constructor(count: number) {
super(new SphereGeometry(0.1), new MeshBasicMaterial(), count);
}
updateParticle(index: number, position: Vector3): void {
this.setMatrixAt(index, new Matrix4().setPosition(position));
this.instanceMatrix.needsUpdate = true;
}
}// shared/utils/ObjectPool.ts
export class ObjectPool<T> {
private available: T[] = [];
private createFn: () => T;
constructor(createFn: () => T, initialSize = 10) {
this.createFn = createFn;
for (let i = 0; i < initialSize; i++) {
this.available.push(createFn());
}
}
acquire(): T {
return this.available.pop() || this.createFn();
}
release(obj: T): void {
this.available.push(obj);
}
}// domain/services/LODService.ts
export class LODService {
static getDetailLevel(distance: number): DetailLevel {
if (distance < 10) return "high";
if (distance < 50) return "medium";
return "low";
}
}// __tests__/domain/services/ParticleService.test.ts
describe("ParticleService", () => {
it("should create particles with correct properties", () => {
const service = new ParticleService();
const particles = service.createParticles(100);
expect(particles).toHaveLength(100);
expect(particles[0]).toHaveProperty("position");
expect(particles[0]).toHaveProperty("velocity");
});
});// __tests__/presentation/hooks/useParticles.test.tsx
describe("useParticles", () => {
it("should update particle positions on frame", () => {
const { result } = renderHook(() => useParticles({ count: 10 }));
act(() => {
// Simulate frame update
jest.advanceTimersByTime(16); // 60fps
});
expect(result.current.particles[0].position).not.toEqual(
new Vector3(0, 0, 0)
);
});
});{
"dependencies": {
"@react-three/fiber": "^8.15.12",
"@react-three/drei": "^9.92.7",
"@react-three/postprocessing": "^2.15.11",
"@react-spring/three": "^9.7.3",
"three": "^0.159.0",
"zustand": "^4.4.7",
"framer-motion-3d": "^10.16.16"
},
"devDependencies": {
"@types/three": "^0.159.0",
"vite-plugin-glsl": "^1.1.2"
}
}Esta arquitectura te proporciona:
- Escalabilidad: Fácil agregar nuevas experiencias 3D
- Mantenibilidad: Separación clara de responsabilidades
- Testabilidad: Cada capa puede testearse independientemente
- Performance: Optimizaciones específicas para 3D
- Type Safety: TypeScript en toda la aplicación
- Reutilización: Componentes y hooks reutilizables
¿Te gustaría que profundice en algún aspecto específico de esta arquitectura?