Roomify: Creando un Visualizador de Interiores con IA usando React y Puter Serverless
Roomify transforma un plano 2D en un renderizado 3D fotorrealista en segundos. La aplicación utiliza React 19 con TypeScript para el frontend y la plataforma Puter para la lógica serverless, almacenamiento y generación de IA. Sin un backend personalizado, el frontend interactúa directamente con la API de Puter a través del SDK puter.js.
La arquitectura elimina el servidor tradicional: las subidas de archivos, la creación de proyectos y las llamadas de IA ocurren en workers. Esto simplifica el despliegue y la escalabilidad para desarrolladores de nivel medio y senior.
Stack Tecnológico y sus Funciones
- React 19 + TypeScript: Un frontend tipado con HMR a través de Vite.
- React Router 7: Rutas para la página de inicio y el visualizador.
- Tailwind CSS: Estilos con utilidades sin CSS personalizado.
- Lucide React: Iconos para la interfaz de usuario.
- Puter.js: Acceso al almacenamiento de archivos, KV, workers y modelos de IA desde el navegador.
Puter proporciona workers serverless, almacenamiento KV y almacenamiento persistente de archivos. Los workers se llaman mediante puter.action, y la IA mediante puter.ai.generate.
Arquitectura: Desde la Subida hasta el Renderizado
Flujo de procesamiento:
- Subir el plano a
puter.file.write. - Obtener una URL pública para el archivo.
- Llamar al worker
project.createpara escribir metadatos en KV. - Generar mediante el worker
ai.render3d: cargar la imagen, crear un prompt, llamar a Claude/Gemini, guardar el resultado. - Actualizar KV con la URL del renderizado y devolverlo al frontend.
El frontend muestra el plano original y el renderizado usando react-compare-slider para la comparación.
Funciones de Worker: Código y Lógica
Las funciones de worker en JavaScript se suben a través de la CLI de Puter.
Creando un proyecto:
// lib/puter.action.ts
export async function createProject({ item, visibility }) {
const result = await puter.action('project.create', {
item,
visibility,
});
return result;
}
export async function getProjects() {
return await puter.action('project.list');
}
export async function getProjectById({ id }) {
return await puter.action('project.get', { id });
}
Generando un renderizado:
// worker: ai.render3d
export default async ({ sourceImage, style = 'fotorrealista' }) => {
const imageUrl = typeof sourceImage === 'string' ? sourceImage : await puter.file.url(sourceImage);
const prompt = `
Transforma este plano de planta en un renderizado 3D de interior ${style}.
La imagen muestra la distribución de una habitación con paredes, ventanas y puertas.
Genera una vista realista desde una perspectiva que muestre mejor el espacio.
Añade muebles, texturas, iluminación y sombras apropiadas.
Haz que parezca una visualización arquitectónica profesional.
`;
const result = await puter.ai.generate({
model: 'claude-3-sonnet',
prompt,
images: [imageUrl],
responseFormat: 'image'
});
const renderedPath = `/projects/${projectId}/rendered.png`;
await puter.file.write(renderedPath, result.image);
const key = `project:${projectId}`;
const project = JSON.parse(await puter.kv.get(key));
project.renderedImage = puter.file.url(renderedPath);
await puter.kv.set(key, JSON.stringify(project));
return {
renderedImage: project.renderedImage,
renderedPath
};
};
KV almacena metadatos: nombre del proyecto, propietario, privacidad y enlaces a archivos.
Frontend: Componentes y Hooks
La página de inicio (Home) muestra una lista de proyectos. El componente Upload convierte un archivo a base64:
// components/Upload.tsx
const handleFileChange = async (e) => {
const file = e.target.files[0];
if (!file) return;
const base64 = await toBase64(file);
onComplete(base64);
};
El visualizador (VisualizerId) usa useEffect para la generación automática:
useEffect(() => {
if (isProjectLoading || hasInitialGenerated.current || !project?.sourceImage) return;
if (project.renderedImage) {
setCurrentImage(project.renderedImage);
hasInitialGenerated.current = true;
return;
}
hasInitialGenerated.current = true;
void runGeneration(project);
}, [project, isProjectLoading]);
useRef evita llamadas duplicadas. El estado es local, sin Redux.
Solucionando Problemas Comunes
- CORS: Proxy de Vite en desarrollo.
- Tamaño de Base64: Guardar el archivo por separado, almacenar el enlace en KV.
- Doble generación:
useRef+ verificaciones. - Asincronía: Estados de carga, esqueletos.
- UI móvil: Responsividad de Tailwind, soporte táctil para el deslizador.
Validación: archivos de hasta 10 MB, JPG/PNG. Indicadores de carga para tiempos de generación de 10-15 segundos.
Conclusiones Clave
- Puter reemplaza el backend: workers, KV, archivos e IA en un solo SDK.
- Los hooks de React gestionan el estado sin bibliotecas externas.
- Los prompts definen la calidad: detallar paredes, muebles e iluminación.
- La arquitectura escala para producción sin DevOps.
- El código es de código abierto, adecuado para bifurcaciones y experimentos.
— Editorial Team
Aún no hay comentarios.