Как я сделал Roomify — AI-визуализатор интерьеров на React и Puter

Привет, Хабр! Меня зовут Андрей, и я фулл-стек-разработчик. Недавно я выпустил свой pet-проект Roomify — веб-приложение, которое превращает обычный план помещения в фотореалистичный 3D-рендер за несколько секунд. В этой статье я хочу рассказать, как всё устроено под капотом: от выбора технологий до интеграции с AI и облачной платформой Puter.

Зачем вообще это нужно?

Представьте: вы архитектор или дизайнер интерьеров. Клиент приносит чертёж и просит «показать, как это будет выглядеть в реальности». Обычно вы запускаете 3ds Max, Blender или SketchUp, часами выставляете свет, текстуры, мебель… А что, если нейросеть сделает это за пару секунд? Roomify — попытка ответить на этот вопрос.

Кроме того, я хотел:

  • Изучить интеграцию с AI-моделями (Claude, Gemini) без разворачивания своего бэкенда.

  • Попробовать платформу Puter как альтернативу классическому серверу.

  • Сделать удобный и красивый интерфейс на React.

Получилось то, чем я горжусь, и теперь я делюсь опытом.

Как я сделал Roomify — AI-визуализатор интерьеров на React и Puter - 1

Выбор стека

Сначала определился с инструментами:

  • React 19 + TypeScript — современный фронтенд со статической типизацией.

  • React Router 7 — маршрутизация для страниц проекта и визуализатора.

  • Vite — быстрая сборка и HMR.

  • Tailwind CSS — утилитарные классы для стилизации, чтобы не писать тонны CSS.

  • Lucide React — красивые иконки.

  • Puter.js — SDK для взаимодействия с платформой Puter.

Почему Puter? Это «интернет-ОС», которая предоставляет serverless workers, KV-хранилище, постоянное файловое хранилище и доступ к AI-моделям прямо из браузера. Мне не пришлось поднимать собственный бэкенд, что сильно упростило разработку.

Как работает Puter в проекте

Вся логика хранения проектов и вызова AI построена на Puter. Я использую несколько ключевых возможностей:

  1. Файловое хранилище — загруженные планы и сгенерированные рендеры сохраняются как файлы. Puter сам генерирует публичные URL, которые потом можно использовать в <img>.

  2. KV-хранилище — для метаданных проекта: имя, время создания, владелец, приватность. Это быстрый key-value доступ, идеально для таких данных.

  3. Serverless Workers — я создал несколько worker-функций, которые вызываются через puter.action. Они обрабатывают загрузку, взаимодействие с AI и возвращают результат.

В коде это выглядит примерно так:

// 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 });
}

Все worker-функции написаны на JavaScript и загружены в Puter через их CLI. В результате я получаю полноценный API, не занимаясь DevOps.

AI-генерация: превращаем 2D-план в 3D-рендер

Сердце приложения — функция generate3DView. Она отправляет изображение плана на AI-модель (Claude или Gemini) и получает обратно сгенерированное изображение интерьера.

Вот упрощённая версия:

Все worker-функции написаны на JavaScript и загружены в Puter через их CLI. В результате я получаю полноценный API, не занимаясь DevOps.

AI-генерация: превращаем 2D-план в 3D-рендер
Сердце приложения — функция generate3DView. Она отправляет изображение плана на AI-модель (Claude или Gemini) и получает обратно сгенерированное изображение интерьера.

Вот упрощённая версия:

На стороне worker я формирую промпт, который описывает, что нужно сделать:

«Преобразуй данный план помещения в фотореалистичное 3D-изображение интерьера. Учти расположение стен, окон, дверей. Добавь текстуры, мебель, освещение в современном стиле.»

Модель генерирует изображение, и worker сохраняет его в файловое хранилище, возвращая мне base64 и публичную ссылку.

Как я сделал Roomify — AI-визуализатор интерьеров на React и Puter - 2

Проблемы, с которыми столкнулся:

  • Ограничение размера входного изображения. Puter и модели не любят слишком большие файлы. Пришлось добавить валидацию на фронте: до 10 МБ, JPG/PNG.

  • Время генерации. Иногда модель думает 10-15 секунд. На фронте я добавил лоадер с анимацией, чтобы пользователь не дёргался.

  • Качество результата. Не всегда получается идеально, особенно если на плане много деталей. Планирую дорабатывать промпты и добавлять выбор стиля.

Фронтенд: компоненты и логика

Приложение состоит из двух основных страниц: главная (Home) и визуализатор (VisualizerId).

Главная страница

Здесь пользователь видит ленту своих проектов и может загрузить новый план. Компонент Upload отвечает за чтение файла и преобразование в base64.

// components/Upload.tsx
const handleFileChange = async (e) => {
  const file = e.target.files[0];
  if (!file) return;
  const base64 = await toBase64(file);
  onComplete(base64);
};

После успешной загрузки вызывается handleUploadComplete из Home. Он создаёт новый проект через createProject, сохраняет его в Puter и перенаправляет на страницу визуализатора с передачей начального изображения.

Особенность: я добавил isCreatingProjectRef.current, чтобы предотвратить повторный вызов, если пользователь быстро кликнет несколько раз.

Страница визуализатора

Здесь происходит магия. Компонент получает id из URL, загружает проект через getProjectById и, если у него ещё нет рендера, автоматически запускает генерацию.

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 для флага hasInitialGenerated, чтобы генерация запускалась ровно один раз, даже если эффект сработает повторно.

Для сравнения «до и после» используется библиотека react-compare-slider. Она позволяет перетаскивать ползунок и видеть разницу между исходным планом и рендером.

<ReactCompareSlider
  itemOne={<ReactCompareSliderImage src={sourceImage} />}
  itemTwo={<ReactCompareSliderImage src={renderedImage} />}
/>

Визуализатор также содержит кнопки экспорта (скачать рендер) и «Поделиться» (пока не реализована, но будет).

Управление состоянием

Состояние проектов на главной странице хранится в локальном стейте, обновляется после загрузки и создания. Никакого Redux не понадобилось — React + хуки справляются отлично.

Проблемы, с которыми пришлось столкнуться

  1. CORS при работе с Puter
    Puter SDK требует правильной настройки origin. В dev-режиме я использовал прокси Vite, чтобы избежать проблем.

  2. Обработка base64
    При сохранении проекта нужно передавать base64 изображения, но Puter имеет ограничение на размер передаваемых данных. Я решил это тем, что сначала сохраняю файл отдельно, а в KV кладу только ссылку. В worker это сделано прозрачно.

  3. Двойная генерация
    Из-за эффектов React и строгого режима в dev-сборке генерация могла запускаться дважды. Исправил с помощью useRef и проверки на наличие уже сгенерированного изображения.

  4. Асинхронная загрузка
    При быстром переходе на страницу проекта данные могли ещё не подтянуться. Добавил состояние isProjectLoading и показываю скелетон (не в текущем коде, но планирую).

  5. UI на мобильных
    Tailwind помог сделать адаптив, но с react-compare-slider на телефонах пришлось повозиться. В итоге уменьшил высоту слайдера и добавил touch-события.

Результат

Roomify полностью работает. Вы можете попробовать демо по ссылке: https://home-room-weld.vercel.app (если ещё не убили мои бесплатные квоты). Исходный код открыт на GitHub. Лицензия позволяет форкать, использовать и дорабатывать.

Как я сделал Roomify — AI-визуализатор интерьеров на React и Puter - 3

Что дальше?

  • Выбор стиля интерьера. Пользователь сможет указать «минимализм», «лофт», «скандинавский» и т.д.

  • Редактирование после генерации. Хочется дать возможность изменять материалы, добавлять/убирать объекты.

  • Поддержка векторных форматов (SVG, DXF) для более точного распознавания.

  • Мобильное приложение на React Native.

  • Интеграция с BIM-системами (например, экспорт в IFC).

Заключение

Roomify показал, что современные технологии позволяют решать сложные творческие задачи с минимальными усилиями. Благодаря React и Puter я создал полноценный сервис всего за пару недель. Главное — не бояться экспериментировать и делиться результатами.

Буду рад, если вы заглянете в репозиторий, поставите звезду или откроете issue с предложениями. А если сами захотите сделать что-то подобное — смело используйте Puter, он реально упрощает жизнь.

Спасибо за внимание!

Автор: Kurganov1993

Источник

Оставить комментарий