Frontend Development 9 min read

Building a Cross‑Platform 3D Desktop Pet with Electron, Vue, and Dexie.js

This article introduces the open‑source “3D Desktop Pet” project, detailing how to create a cross‑platform Electron application that combines a transparent always‑on‑top window, Three.js‑driven 3D pet interactions, and Dexie.js‑based task management, along with performance optimizations and multi‑window reminder system.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Building a Cross‑Platform 3D Desktop Pet with Electron, Vue, and Dexie.js

When Anime Meets Productivity Tools

Recently I developed a cross‑platform desktop app called “小桌宠” (Little Desktop Pet) that merges a 3D pet with a task‑management system. The rabbit not only looks cute but also pops up reminders when you slack off, acting as a cyber‑supervisor.

Project is Open‑Source – Star It!

Repository: https://github.com/zlmica/3d-desktop-pet

Pre‑built installers for multiple platforms are available in the video description.

1. Transparent Window & Cross‑Process Communication (Core Tech)

Effect: The pet stays on top, and a right‑click menu can launch task/reminder windows.

function createWindow() {
  const primaryDisplay = screen.getPrimaryDisplay()
  const { width: screenWidth, height: screenHeight } = primaryDisplay.workAreaSize

  win = new BrowserWindow({
    icon: path.join(process.env.VITE_PUBLIC, 'rabbit.png'),
    webPreferences: { preload: path.join(__dirname, 'preload.mjs') },
    width: 180,
    height: 200,
    x: screenWidth - 200,
    y: screenHeight - 150,
    autoHideMenuBar: true,
    transparent: true,
    frame: false,
    alwaysOnTop: true,
    resizable: false,
    hasShadow: false,
  })

  win.webContents.on('did-finish-load', () => {
    win?.webContents.send('main-process-message', new Date().toLocaleString())
  })

  if (VITE_DEV_SERVER_URL) {
    win.loadURL(VITE_DEV_SERVER_URL)
  } else {
    win.loadFile(path.join(RENDERER_DIST, 'index.html'))
  }

  win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true })
}

Key code points:

transparent: true + frame: false → borderless transparent window

alwaysOnTop: true → keep window on top

IPC communication controls child‑window state

Pitfall tip: Click‑through issues are solved by calculating mouse position and dynamically adjusting the window size.

2. 3D Pet Interaction System (TresJS Magic)

Model loading & animation control:

const isPlaying = ref(false)

const play = async () => {
  currentAction2.value.play()
  isPlaying.value = true
}

const sleep = () => {
  currentAction2.value.stop()
  isPlaying.value = false
}

const hello = () => {
  if (isPlaying.value) {
    currentAction2.value.stop()
  }
  currentAction.value.reset()
  currentAction.value.play()
  currentAction.value.setLoop(THREE.LoopOnce, 1)
  currentAction.value.clampWhenFinished = true

  currentAction.value.getMixer().addEventListener('finished', () => {
    currentAction.value.stop()
    if (isPlaying.value) {
      currentAction2.value.play()
    }
  })
}

GLTFLoader loads skeletal‑animation models

AnimationMixer switches between greeting, moving, and idle states

Click‑through handling converts 3D coordinates to screen coordinates

Performance tweaks: idle state drops frame rate (30 FPS → 15 FPS) and rendering pauses when the window is off‑screen.

3. Task Management System (Dexie.js in Action)

Database design:

class PetAppDB extends Dexie {
  tasks!: Table
reminders!: Table
constructor() {
    super('petApp')
    this.version(1).stores({
      tasks: '++id, title, status, priority, createdAt, updatedAt, dueDate',
      reminders: '++id, title, reminderTime, repeatType, isEnabled, lastAcknowledgedAt, createdAt',
    })
  }

  async addTask(task) {
    const timestamp = Date.now()
    return await this.tasks.add({ ...task, createdAt: timestamp, updatedAt: timestamp })
  }

  async updateTask(id, changes) {
    return await this.tasks.update(id, { ...changes, updatedAt: Date.now() })
  }
  // …other methods
}

Smart sorting: unfinished > with due date > creation time

Auto‑expire detection via dueDate + timer

Offline storage: IndexedDB with local backup

Typical query example – tasks due in the next 24 hours:

// Get tasks whose dueDate is within the next 24 h
const upcomingTasks = await db.tasks
  .where('dueDate')
  .between(Date.now(), Date.now() + 86400000)
  .toArray()

4. Reminder System (Multi‑Window Coordination)

Architecture design:

export function useReminder() {
  async function checkReminders() {
    const reminders = await db.checkReminders()
    reminderQueue.value = reminders
    if (reminderQueue.value.length > 0) {
      window.ipcRenderer.send('show-reminder-window')
    }
  }
}

Main window polls for pending reminders

Reminder window manages a queue and shows animated cards

IPC messages: show-reminder-window / hide-reminder-window

Animation implementation: Vue TransitionGroup + CSS keyframes create slide‑in card effects.

5. Performance Optimization Practices

Window management: single‑instance child windows to avoid memory leaks

3D rendering: off‑screen canvas + on‑demand rendering

Database: batch operations + index tuning

Packaging: electron‑builder multi‑platform configuration

{
  $schema: "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json",
  appId: "YourAppID",
  asar: true,
  productName: "小桌宠",
  directories: { output: "release/${version}" },
  files: ["dist", "dist-electron"],
  mac: { target: ["dmg"], artifactName: "${productName}-Mac-${version}-Installer.${ext}" },
  win: { target: [{ target: "nsis", arch: ["x64"] }], artifactName: "${productName}-Windows-${version}-Setup.${ext}" },
  nsis: { oneClick: false, perMachine: false, allowToChangeInstallationDirectory: true, deleteAppDataOnUninstall: false },
  linux: { target: ["AppImage"], artifactName: "${productName}-Linux-${version}.${ext}" }
}

Conclusion – Reflections on Desktop Development

The project demonstrates that Electron is a viable choice for modern desktop applications, offering rapid development and strong cross‑platform capabilities despite larger bundle sizes. Future plans include adding a pet‑swap system and more interactive features.

Read the follow‑up article about the pet‑swap feature: https://juejin.cn/post/7475544117415624739

Side note: integrating AI into the project saved a lot of development time – it’s simply awesome!

Cross‑PlatformElectronVueThree.jsdesktop-appDexie.js
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.