Voxii is an immersive virtual reality language learning application designed for Windows desktop. Step into lifelike scenarios—like ordering coffee in a bustling café or navigating a vibrant marketplace—and engage in realistic, conversational interactions that make learning a new language both effective and enjoyable.
- 🎭 Life-like Scenes: Immerse yourself in diverse environments to practice speaking naturally.
- 🗣️ Real-Life Scenarios: Engage in common situations to enhance your English conversational skills.
- 🎤 Speech Recognition & Generation: Speak directly into your microphone and receive real-time responses from the Voxii avatar.
- ✅ Avatar Corrections: Receive polite and constructive feedback on any mistakes during conversations.
- 📊 Score & Corrections: Access immediate feedback, improvement tips, and track your conversation scores with a single button click.
Before you begin, ensure you have the following:
- Operating System: Windows 10 or higher
- Hardware: Meta Quest 3 headset and controllers
- Software:
- API Key: Obtain an API key from Groq
-
Clone the Repository:
git clone https://github.com/YourUsername/Voxii.git cd Voxii -
Configure the API Key:
- Create a
.envfile in the root directory of the project. - Add your Groq API key in the following format:
GROQ_API_KEY=<YOUR_GROQ_API_KEY_HERE>
- Create a
-
Install Dependencies:
- Open the project in Unity.
- Let Unity import all necessary packages and dependencies.
-
Launch the Application:
- Press the Play button in Unity to start the application.
- Controls:
- Primary Button (X or A): Hold to start speaking.
- Secondary Button (Y or B): Press to receive feedback on your conversation.
-
API Key Issues:
- Ensure the
.envfile is correctly placed in the root directory. - Verify that the API key is valid and has not expired.
- Ensure the
-
Controller Not Responding:
- Check the connection between your VR controllers and the desktop.
- Ensure the controllers are properly paired and recognized by the system.
-
Speech Recognition Errors:
- Confirm that your microphone is functioning and properly configured.
- Test your microphone with other applications to ensure it's working correctly.
Ready to dive in? Follow these simple steps to start your language learning journey with Voxii:
-
Set Up Your Environment:
- Ensure all prerequisites are met.
- Configure your
.envfile with the Groq API key.
-
Launch Voxii:
- Open the project in Unity.
- Click the Play button to start the application.
-
Begin Learning:
- Hold the Primary Button (X or A) on your controller to initiate conversation.
- Engage with the Voxii avatar in various scenarios.
- Press the Secondary Button (Y or B) to receive feedback and track your progress.
-
Monitor Your Progress:
- View your conversation scores and error corrections in real-time.
- Use the feedback to improve your language skills continuously.
| Description | Demo |
|---|---|
| 👉 Click the image to watch the demo video | ![]() |
| 👉 Click the image to view the pitch deck | ![]() |
Note: Only public methods are included here.
- AI 🤖: Contains the AI avatar's logic and the Grammar Checker AI logic
- LevelManagement 🎮: Contains the logic for managing the various scenes from the main menu to the post-level screen
- Utility 🛠️: Contains utility scripts like the
SendWavFilefor saving audio clips as.wavfiles
AIVoice.cs (static class) 🎤
Description:
Text to speech for the AI avatar
static async Task Speak(string msg)- Asynchronously outputs AI speech for the given message using Piper TTS
static async Task SpeakRepeat()- Outputs the "Can you repeat that" message audio
static async Task SpeakInitialMsg()- Outputs the initial message for the specific avatar
ChatLoop.cs 🔄
Description:
The user-avatar conversation loop
GroqApiClient groqApibool isResponding: Tracks if the AI is respondingbool userSpeaking: Tracks if the user is speakingstring chatLogFilePath: File path for chat logsGameObject loadingSymbolint msgsSentTextMeshProUGUI messagesRemainingValueLevelManagement levelManagement
async Task SendUserMessage(string msg)- Sends a message to the AI avatar, adds it to logs and chat history
void setIsResponding(bool value)- Setter for
isResponding
- Setter for
void setUserSpeaking(bool value)- Setter for
userSpeaking
- Setter for
GroqApiClient.cs 📡
Description:
Handles requests sent to Groq's llama3
GroqApiClient(string apiKey = "")- Initializes the Groq API client with an API key or from a
.envfile
- Initializes the Groq API client with an API key or from a
async Task<JObject?> CreateChatCompletionAsync(JObject request)- Sends a request to the AI, including parameters and chat history
Scorer.cs 📝
Description:
Contains the AI scoring logic for the user's English
GroqApiClient groqApi
Scorer(string chatLogFilePath)- Initializes the scorer with the chat logs of the conversation so far
async Task<string> GetScore()- Gets the score on the user's English of the conversation
async Task<string> GetResponseOutput(JArray msgs)- Overload the GetResponseOutput method to accept a JArray parameter
- Useful for testing the scoring system with different chat logs
ScoreResult ParseScoreNumbers(string scoreString)- Parses the score string to extract the number of errors and accuracy
SentimentResult ParseSentiment(string scoreString)- Parses the sentiment from the AI response
public List<ErrorExample> ParseErrorExamples(string scoreString)- Parses the error examples from the AI response
float CalculateResponseTime(string chatLogFilePath)- Calculates the time taken for the user to respond to the AI
static int CalculatePoints(ScoreResult scoreResult, float responseTime)- Calculates the points based on the score result
async Task<int> CalculatePointsAsync()- Step-by-step calculation of points
async Task<(ScoreResult, float, List<ErrorExample>)> GetResultsAndResponseTimeAsync- Gets ScoreResult object
class ScoreResult- Holds data needed for score result (NumberOfErrors, Accuracy)
class SentimentResult- Holds data needed for sentiment result (Sentiment)
class ErrorExample- Holds data needed for error examples (Category, Incorrect, Corrected, Reasoning)
WhisperTranscriber.cs 🗣️
Description:
Contains the logic for transcribing the user's speech to text
TextMeshProUGUI displayText
async Task<string> TranscribeRecording()- Retrieves the string for the user's speech stored in
recording.wavin the application's persistent data path
- Retrieves the string for the user's speech stored in
SentimentAnalyzer.cs 😊
Description:
Analyzes the sentiment of the AI response each time the AI responds
Used to update the animation of the AI avatar
public async Task<bool> IsPositiveOrNeutralSentiment(string message)- Sends the AI avatar's response to the Groq API to analyze the sentiment
- Returns
trueif the sentiment is positive or neutral,falseotherwise
OnboardingData.cs 📄
Description:
Data container class to hold onboarding data instances
string PersonName { get; set; }string LanguageProficiency { get; set; }string LanguageToLearn { get; set; }List<string> PhrasesToWorkOn { get; set; }string Scene { get; set; }Dictionary<string, string> SceneToRole { get; set; }
FeedbackScreenController.cs 💬
Description:
Controls the feedback screen
GameObject screen1GameObject screen2Button nextButtonButton doneButtonTextMeshProUGUI pointValueTextMeshProUGUI grammarErrorValueTextMeshProUGUI responseTimeValueTextMeshProUGUI relevanceValueTextMeshProUGUI feedbackCategoryTextMeshProUGUI feedbackIncorrectTextMeshProUGUI feedbackCorrectedTextMeshProUGUI feedbackReasoning
void ShowSecondScreen()void ChangeToMenuScene()
LevelManagement.cs ↔️
Description:
Switches between the main menu and the post-level screen displays based on the scene
GameObject display1GameObject display2
void goToMainMenu()void goToPostLevel()void switchDisplays()
MenuData.cs 🗂️ (static class - WavUtility)
Description:
Handles menu data storage and retrieval
static List<bool> OptionsSelectedstatic string SceneSelectionstatic float LanguageProficiencystatic float AvatarHostilitystatic string filePath
static void SetFilePath(string appDataPath)- Needed because
Application.persistentDataPathcan't be accessed by a static non-MonoBehavior class
- Needed because
static void SaveDataToJson()static void LoadDataFromJson()static string getRole()- Retrieves the role based on the scene
class MenuDataModel- Stores necessary data (
OptionsSelected,SceneSelection,LanguageProficiency,AvatarHostility)
- Stores necessary data (
ResultsData.cs 📊
Description:
Class to store result data
static int pointsstatic int errorsstatic int relevanceScorestatic int responseTimestatic string feedbackCategorystatic string feedbackIncorrectstatic string feedbackCorrectedstatic string feedbackReasoning
XRInputActions.inputactions 🎮
Description:
Contains the mappings which map from the keyboard to the controller's controls
void StartLoading()- Initializes the loading icon logic
LoadingSymbolController.cs ⏳
Description:
Handles the loading symbol logic
GameObject loadingSymbol
void ShowLoadingSymbol()- Displays the loading symbol
void HideLoadingSymbol()- Hides the loading symbol
MicRecorder.cs 🎙️
Description:
Handles recording through the microphone
GameObject loadingSymbol
void StartRecording()- Starts recording the microphone
void StopRecording()- Stops recording the microphone
void SaveRecording()- Saves the recording in the persistent data path as
recording.wav
- Saves the recording in the persistent data path as
void PlayRecording()- Plays the recording
PrimaryBtnHold.cs ▶️
Description:
Logic for actions when the primary button is held
InputActionAsset inputActionAssetMicRecorder micRecorderWhisperTranscriber whisperTranscriberChatLoop chatLoop
bool isRecording: Tracks whether the application is recording
SecondaryBtnPress.cs 🔁
Description:
Logic for actions when the secondary button is pressed
InputActionAsset inputActionAssetChatLoop chatLoopPrimaryBtnHold primaryBtnHold
SaveWavFile.cs 💾 (static class - WavUtility)
Description:
Contains logic to save an audio clip in .wav format
static byte[] FromAudioClip(AudioClip clip)- Converts an audio clip into
byte[]
- Converts an audio clip into
static void SaveWav(string filePath, AudioClip clip)- Saves the audio clip as a
.wavfile to the specified path
- Saves the audio clip as a
BackgroundMusicController.cs 🎵
Description:
Controls the background music
Button muteButtonGameObject symbol: Symbol to show the mute buttonSprite unmutedImageSprite mutedImage
void ToggleMute()void UpdateButtonImage()- Changes the button image based on the mute state

