Skip to content

Commit

Permalink
feat: animations by audio recording status
Browse files Browse the repository at this point in the history
  • Loading branch information
mikejgray committed Dec 22, 2023
1 parent 5d44f6b commit a99d45d
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 2 deletions.
2 changes: 1 addition & 1 deletion neon_iris/models/web_sat.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""API data models for the WebSAT API."""
# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
# All trademark and other rights reserved by their respective owners
# Copyright 2008-2021 Neongecko.com Inc.
# Copyright 2008-2024 Neongecko.com Inc.
# BSD-3
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
Expand Down
Empty file removed neon_iris/static/scripts/main.js
Empty file.
20 changes: 20 additions & 0 deletions neon_iris/static/scripts/sprite.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const sprite = document.querySelector(".sprite");

function triggerWake() {
sprite.classList.remove("record", "waiting");
sprite.classList.add("wake");
}

function triggerRecord() {
sprite.classList.remove("wake", "waiting");
sprite.classList.add("record");
}

function triggerWaiting() {
sprite.classList.remove("wake", "record");
sprite.classList.add("waiting");
}

function triggerDone() {
sprite.classList.remove("wake", "record", "waiting");
}
2 changes: 2 additions & 0 deletions neon_iris/static/scripts/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ function submitMessage() {

async function getAIResponse(text = "", recording = "") {
try {
triggerWaiting(); // Trigger waiting animation
const payload =
text !== "" && recording === ""
? { utterance: text }
Expand Down Expand Up @@ -46,6 +47,7 @@ async function getAIResponse(text = "", recording = "") {
// Add in the user's transcription if STT
if (text === "" && recording !== "") {
const userMessage = createMessageDiv("user", data.utterance);
triggerDone(); // Trigger done animation
appendMessageToHistory(userMessage);
saveMessageToLocalStorage("user", data.utterance);
}
Expand Down
3 changes: 3 additions & 0 deletions neon_iris/static/scripts/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ async function handleSpeechEnd(audio) {
downloadLink.download = "recorded_audio.wav";
downloadLink.textContent = "Download Recorded Audio";
downloadArea.appendChild(downloadLink);
triggerWaiting(); // Trigger waiting animation
} else {
console.error("Download area not found");
}
Expand Down Expand Up @@ -131,13 +132,15 @@ const WebSocketHandler = (() => {
audio.onended = () => {
console.log("Activation sound is done playing");
if (myVad && !isVadRunning) {
triggerRecord(); // Trigger recording animation
myVad.start();
isVadRunning = true;
} else if (!shouldListen && isVadRunning) {
myVad.pause();
isVadRunning = false;
}
};
triggerWake(); // Trigger wake animation
audio.play();
lastActivationTime = currentTime;
}
Expand Down
61 changes: 61 additions & 0 deletions neon_iris/static/sprite.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* Base styles for the split sprite */
.sprite {
width: 40px; /* sprite width adjusted to 40px */
height: 40px; /* sprite height adjusted to 40px */
border-radius: 50%;
background-image: linear-gradient(to right, #000 50%, #fff 50%);
box-shadow: 0 0 0 2px #000; /* Adjusted border thickness for smaller size */
transition: transform 0.3s ease, opacity 0.3s ease;
opacity: 0; /* sprite is invisible by default */
position: relative; /* Required for absolute positioning of pseudo-elements */
display: flex; /* Center content */
justify-content: center;
align-items: center;
margin: 20px;
}

/* Pulse animation while recording */
@keyframes pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
}

.sprite.record {
animation: pulse 1s infinite ease-in-out;
opacity: 1; /* sprite is visible while recording */
}

/* Spin animation while waiting for a response */
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

.sprite.waiting {
animation: spin 2s infinite linear;
opacity: 1; /* sprite is visible while waiting */
}

/* Appear animation for wake word activation */
@keyframes appear {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

.sprite.wake {
animation: appear 1s forwards;
opacity: 1; /* sprite is visible when awake */
}
4 changes: 4 additions & 0 deletions neon_iris/static/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ html {
margin: 0;
padding: 0;
}
img.logo {
width: 40px;
height: 40px;
}
a {
text-decoration: none;
}
Expand Down
5 changes: 4 additions & 1 deletion neon_iris/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ title }}</title>
<link rel="stylesheet" href="/static/styles.css" />
<link rel="stylesheet" href="/static/sprite.css" />
</head>
<body>
<div class="chat-container">
<div class="chat-header">
{{ description }} <img src="/static/logo.webp" />
{{ description }} <img src="/static/logo.webp" class="logo" />
</div>
<button id="startButton">Start Recording</button>
<div class="chat-window" id="chatWindow">
Expand All @@ -22,6 +23,7 @@
<div class="input-area">
<input type="text" id="chatInput" placeholder="{{ placeholder }}" />
<button onclick="submitMessage()">Submit</button>
<div class="sprite" />
</div>
</div>
</body>
Expand All @@ -36,4 +38,5 @@
<script src="/static/scripts/websocket.js"></script>
<script src="/static/scripts/audio.js"></script>
<script src="/static/scripts/ui.js"></script>
<script src="/static/scripts/sprite.js"></script>
</html>

0 comments on commit a99d45d

Please sign in to comment.