vue-moodie
v1.0.0
Published
A customizable, animated mood-reactive mascot widget for Vue 3
Maintainers
Readme
vue-moodie
A customizable, animated mood-reactive mascot widget for Vue 3.
Moodie is a little ghost-like creature that reacts to your app's state with expressions, speech bubbles, animations, hats, and more — all without any extra dependencies beyond Vue 3 itself.
Features
- Mood system —
happy,sad,excited,scared,angry,neutral,sleeping,dead - Speech bubble — display any message above Moodie
- Mouse tracking — eyes and body smoothly follow the cursor (lerp)
- Idle & sleep — automatically falls asleep after inactivity
- Animations — plane flight, bounce, peek, rope swing (triggered programmatically or via keyboard)
- Hats —
cowboy,propeller,helmet, or none - Size —
baby,medium,adult - Color picker — built-in HUD with color presets + custom hex input
- Kill / revive — tombstone animation when "killed", spring back to life on revive
- Light theme — pulls on sunglasses and triggers a flash effect
- Keyboard shortcuts —
Shift+Kkill,Shift+Rrevive,Shift+Aanimate,Shift+Ssize cycle,Shift+Hhat cycle useMoodiecomposable — drive Moodie's state from your own logic (sliders, sensors, API data)MoodieHud— standalone HUD panel you can mount anywhere separately- No Vuetify, no Pinia, no extra dependencies — just Vue 3
Screenshots
Installation
npm install vue-moodiePeer dependency: Vue 3 (
^3.0.0) must already be installed.
Quick Start
Option A — Global plugin
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import VueMoodiePlugin from 'vue-moodie'
import 'vue-moodie/dist/vue-moodie.css'
createApp(App).use(VueMoodiePlugin).mount('#app')<template>
<VueMoodie mood="happy" message="Hello world!" />
</template>Option B — Direct import (no plugin)
<script setup>
import { VueMoodie } from 'vue-moodie'
import 'vue-moodie/dist/vue-moodie.css'
</script>
<template>
<VueMoodie
color="#7c3aed"
size="medium"
hat="propeller"
mood="excited"
message="Let's go! 🚀"
:follow-mouse="true"
:show-hud="true"
/>
</template>Option C — With useMoodie composable
Drive Moodie's state from your own data — perfect for sliders, API results, sensor feeds, etc.
<script setup>
import { ref } from 'vue'
import { VueMoodie, useMoodie } from 'vue-moodie'
import 'vue-moodie/dist/vue-moodie.css'
const { mood, message, react, celebrate, speak } = useMoodie()
const stressScore = ref(5)
function onSliderChange(val) {
stressScore.value = val
// react(value, max, higherIsBetter)
react(val, 10, false) // 10 = max, false = higher stress is worse
}
function onSuccess() {
celebrate()
speak('You did it! 🎉')
}
</script>
<template>
<input type="range" min="0" max="10" v-model="stressScore" @input="onSliderChange($event.target.value)" />
<VueMoodie :mood="mood" :message="message" />
</template>Option D — Separate MoodieHud panel
Mount the HUD anywhere in your layout, independently of where Moodie lives:
<script setup>
import { ref } from 'vue'
import { VueMoodie, MoodieHud } from 'vue-moodie'
import 'vue-moodie/dist/vue-moodie.css'
const color = ref('#7c3aed')
const size = ref('medium')
const hat = ref('none')
const dead = ref(false)
</script>
<template>
<!-- Moodie in one part of the layout -->
<VueMoodie
:color="color"
:size="size"
:hat="hat"
@kill="dead = true"
@revive="dead = false"
/>
<!-- HUD in another part of the layout (e.g. sidebar) -->
<MoodieHud
v-model:color="color"
v-model:size="size"
v-model:hat="hat"
:is-dead="dead"
@kill="dead = true"
@revive="dead = false"
/>
</template>API
VueMoodie Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| color | String | '#7c3aed' | Body fill color (hex) |
| size | String | 'medium' | 'baby' | 'medium' | 'adult' |
| hat | String | 'none' | 'none' | 'cowboy' | 'propeller' | 'helmet' |
| mood | String | 'neutral' | 'neutral' | 'happy' | 'sad' | 'excited' | 'scared' | 'angry' | 'sleeping' | 'dead' |
| message | String | '' | Text shown in the speech bubble (empty = hidden) |
| lightTheme | Boolean | false | When true, Moodie pulls on sunglasses + flash animation |
| idleTimeout | Number | 7000 | Milliseconds before Moodie falls asleep |
| followMouse | Boolean | true | Enable/disable cursor tracking |
| showHud | Boolean | true | Show/hide the built-in HUD control buttons |
VueMoodie Events
| Event | Payload | Description |
|-------|---------|-------------|
| kill | — | Emitted when Moodie is killed (via HUD or keyboard) |
| revive | — | Emitted when Moodie is revived |
| animation | String \| null | Emitted when an animation starts ('plane', 'bounce', 'peek', 'rope') or ends (null) |
MoodieHud Props & Events
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| color | String | '#7c3aed' | Current body color |
| size | String | 'medium' | Current size |
| hat | String | 'none' | Current hat |
| isDead | Boolean | false | Reflects kill/revive state |
Emits: update:color, update:size, update:hat, kill, revive — all compatible with v-model:*.
useMoodie() Composable
const {
mood, // Ref<string> — current mood
message, // Ref<string> — current speech bubble text
lastChanged, // Ref<Date> — timestamp of last state change
react(value, max, higherIsBetter), // Map numeric value to mood + message
reactSlider(value, max, stressor), // React with named stressor context
celebrate(), // Set mood to 'excited' + party message
setIdle(), // Set mood to 'neutral' + clear message
speak(text), // Set message without changing mood
} = useMoodie()react(value, max, higherIsBetter?)
Maps a numeric value to a mood automatically:
react(8, 10, false) // high = bad → mood: 'angry', message: stress warning
react(9, 10, true) // high = good → mood: 'excited', message: congrats
react(3, 10, false) // low = good → mood: 'happy'reactSlider(value, max, stressor)
Same as react but takes a named stressor string for context-aware messages:
reactSlider(4, 5, 'study_load') // mood: 'scared', message: study pressure warning
reactSlider(1, 5, 'sleep_quality') // mood: 'happy', message: great sleep!Supported stressor names: headache, breathing_problem, noise_level, peer_pressure, bullying, study_load, future_career_concerns, sleep_quality.
Keyboard Shortcuts
When focus is anywhere in the page, these global shortcuts control Moodie:
| Shortcut | Action |
|----------|--------|
| Shift + K | Kill Moodie |
| Shift + R | Revive Moodie |
| Shift + A | Trigger random animation |
| Shift + S | Cycle size (baby → medium → adult) |
| Shift + H | Cycle hat (none → cowboy → propeller → helmet) |
UMD / CDN Usage
<link rel="stylesheet" href="https://unpkg.com/vue-moodie/dist/vue-moodie.css" />
<script src="https://unpkg.com/vue/dist/vue.global.js"></script>
<script src="https://unpkg.com/vue-moodie/dist/vue-moodie.umd.cjs"></script>
<div id="app">
<vue-moodie mood="happy" message="Hi from CDN!" />
</div>
<script>
const { createApp } = Vue
createApp({}).use(VueMoodie.default).mount('#app')
</script>Local Development
git clone https://github.com/gmijo47/vue-moodie.git
cd vue-moodie
npm install
npm run build # outputs to dist/
npm run dev # watch modeTo use a local build in another project:
// package.json of your project
{
"dependencies": {
"vue-moodie": "file:../vue-moodie"
}
}Or with a Vite alias:
// vite.config.js
import { resolve } from 'path'
export default {
resolve: {
alias: {
'vue-moodie': resolve(__dirname, '../vue-moodie/src/index.js')
}
}
}Bundle Size
| File | Size | Gzip |
|------|------|------|
| vue-moodie.es.js (ESM) | 32.76 kB | 8.84 kB |
| vue-moodie.umd.cjs (UMD) | 29.27 kB | — |
| vue-moodie.css | 9.35 kB | — |
