A Step-by-Step Guide to Create a Speaking Practice App with AWS Bedrock
Have you ever felt that rock climbing is easier than starting a conversation? Or does an unexpected speaking invitation make your mind go blank or cause you to sweat, even in November? If so, you're not alone. This is called Glossophobia or Speaking Anxiety, which is the most common phobia. Several treatments are available, and you should consult a specialist if it affects your daily life.
In this article, we'll explore how AI can help us create an application to address this issue using AWS Bedrock.
[NOTE: This app doesn't treat the issue. It is just for developing good speaking habits and providing tips.]
Why a Speaking Practice App?
It is proven that knowing your material beforehand, rehearsing your scripts until you have them memorized, and continuing to practice until you are comfortable with what you say can help treat speaking anxiety.
Why AWS Bedrock?
Amazon Bedrock is a fully managed service that provides high-performing foundation models (FMs) from top AI companies like AI21 Labs, Anthropic, Cohere, Meta, Mistral AI, Stability AI, and Amazon through a single API. It offers the capabilities needed to build generative AI applications with security, privacy, and responsible AI. It is also easy to use and beginner-friendly. By using AWS Bedrock, we can ensure our app is efficient, reliable, and easy to integrate with other AWS services.
Prerequisites
To follow along with this tutorial, you will need:
Basic knowledge of JavaScript/ES6
Node.js and npm installed on your machine
AWS credentials (access key ID and secret access key)
Familiarity with AWS Bedrock
Access to the Claude-v2 model
Let's Begin!
Setting Up the Project
First, let's set up our project on the local machine:
Create a new directory and navigate into it:
mkdir speaking-practice-app
cd speaking-practice-app
Initialize a new Node.js project:
npm init -y
Install the required dependencies: Install Vite
for fast development and @aws-sdk/client-bedrock-runtime
for running inference using Amazon Bedrock models.
npm install vite @aws-sdk/client-bedrock-runtime
Creating the User Interface
For the next steps, you can either follow the instructions or clone the entire application from here: https://github.com/amelia2802/glossocure
Create anindex.html
file and add the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Speaking Practice App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>GLOSSOCURE</h1>
</header>
<main id="app">
<section id="daily-text">
<section class="head-component">
<h2>Read Aloud ๐</h2>
<button id="getNewParagraph">Get New Paragraph</button>
</section>
<article id="paragraph">...</article>
</section>
<section class="daily-tips">
<h3>Daily Tips ๐ก</h3>
<p id="tips"></p>
</section>
</main>
<script type="module" src="main.js"></script>
</body>
</html>
Add CSS styling instyle.css
:
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.968);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
display: block;
}
header {
position: absolute;
top: 0;
width: 100%;
text-align: center;
box-shadow: 10px 0 10px rgb(243, 239, 61);
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
#app {
position: absolute;
top: 10rem;
margin: 0 7rem;
}
.head-component {
display: flex;
justify-content: space-between;
}
#getNewParagraph {
height: 100%;
padding: 1rem;
border-radius: 25px;
background-color: rgb(240, 248, 151);
font-weight: 600;
font-family: inherit;
}
#getNewParagraph:hover, #getNewParagraph:focus {
background-color: rgb(231, 246, 76);
box-shadow: 1px 1px 5px rgb(249, 255, 186);
color: black;
}
h2 {
font-size: 1.5rem;
font-style: oblique;
}
#paragraph {
line-height: 27px;
}
.daily-tips {
margin: auto;
width: 30%;
border: 2px dashed rgb(224, 248, 151);
margin-top: 3rem;
border-radius: 1rem 1rem 13%;
text-align: center;
padding: 0 1rem;
}
Integrating the Frontend with the Backend
Now let's link our frontend to AWS Bedrock to fetch paragraphs and tips.
Create amain.js
file & import dependencies
import './style.css';
import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
Setting Up Constants
const modelId = 'anthropic.claude-v2';
const paragraphPrompt = "Generate a paragraph for speaking practice. Imagine you are answering an interview(news,job,entertainment) question.";
const tips = "Give daily two liner tip to overcome speaking anxiety.";
modelId
: The ID of the model we'll use from AWS Bedrock.paragraphPrompt
: A prompt that asks the model to generate a paragraph for speaking practice.tips
: A prompt that asks the model to provide daily tips for overcoming speaking anxiety.
Creating Conversation Objects & Declaring Variables
const paragraphConversation = [
{
role: "user",
content: [{ text: paragraphPrompt }],
},
];
const tipsConversation = [
{
role: "user",
content: [{ text: tips }],
},
];
let client = null;
paragraphConversation
andtipsConversation
: These are the messages we send to the model. Therole: "user"
indicates that the prompt is from the user.client
: This variable will hold the instance ofBedrockRuntimeClient
.
Fetching a New Paragraph & a New Tip
async function fetchNewParagraph() {
disableButton(true);
showLoadingAnimation();
try {
const response = await client.send(new ConverseCommand({ modelId, messages: paragraphConversation }));
console.log("Response from Bedrock (Paragraph):", response);
const paragraph = response.output.message.content[0].text;
document.querySelector("#paragraph").innerHTML = paragraph;
} catch (err) {
console.error("Error fetching new paragraph:", err);
document.querySelector("#paragraph").innerHTML = 'An error occurred while fetching the paragraph.';
}
disableButton(false);
}
async function fetchNewTip() {
try {
const response = await client.send(new ConverseCommand({ modelId, messages: tipsConversation }));
console.log("Response from Bedrock (Tips):", response);
const tip = response.output.message.content[0].text;
document.querySelector("#tips").innerHTML = tip;
} catch (err) {
console.error("Error fetching daily tip:", err);
document.querySelector("#tips").innerHTML = 'Unable to fetch daily tip.';
}
}
fetchNewParagraph
: An asynchronous function that fetches a new paragraph.disableButton(true)
: Disables the button while fetching.showLoadingAnimation()
: Shows a loading spinner while fetching.try-catch
block: Attempts to fetch a new paragraph and update the DOM. If an error occurs, it logs the error and shows an error message.
fetchNewTip
: Similar tofetchNewParagraph
, but it fetches a tip instead.
Utility Function
function disableButton(isDisabled) {
const paragraphButton = document.querySelector("#getNewParagraph");
paragraphButton.disabled = isDisabled;
}
disableButton
: Enables or disables the button to prevent multiple clicks during the fetch.
Initializing the App
async function init() {
try {
const creds = await fetchCredentials();
client = await createBedrockClient(creds);
await fetchNewParagraph();
await fetchNewTip();
} catch (err) {
console.error("Error initializing application:", err);
document.querySelector("#paragraph").innerHTML = 'An error occurred while initializing the application.';
document.querySelector("#tips").innerHTML = 'An error occurred while initializing the application.';
}
const paragraphButton = document.querySelector("#getNewParagraph");
paragraphButton.addEventListener("click", fetchNewParagraph);
}
init
: An asynchronous function to initialize the app.Fetches credentials and creates the Bedrock client.
Fetches the initial paragraph and tip.
Adds an event listener to the button to fetch a new paragraph when clicked.
Creating Bedrock Client
async function createBedrockClient(creds) {
try {
return new BedrockRuntimeClient({
credentials: creds.credentials,
region: creds.region,
});
} catch (err) {
console.error("Error creating Bedrock client:", err);
throw err;
}
}
createBedrockClient
: Creates and returns a new instance of BedrockRuntimeClient
with the provided credentials and region.
Fetching Credentials
async function fetchCredentials() {
return {
region: "us-west-2",
credentials: {
accessKeyId: import.meta.env.VITE_AWS_ACCESS_KEY_ID,
secretAccessKey: import.meta.env.VITE_AWS_SECRET_ACCESS_KEY,
},
};
}
init();
fetchCredentials
: Returns the AWS credentials from environment variables and init()
: Calls the init
function to start the application.
Create a.env
file & add the AWS credentials to it:
VITE_AWS_DEFAULT_REGION=us-west-2 // YOUR REGION
VITE_AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY
VITE_AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY
Running the Application
npx vite
You should now see the app running, with functionalities to fetch new paragraphs for practice and daily tips.
Click on the Generate button and you can see a new paragraph.
Conclusion
Congratulations! You have successfully created a speaking practice app using AWS Bedrock. This app generates paragraphs for speaking practice and daily tips to overcome speaking anxiety, providing a useful tool for improving communication skills.
Subscribe to my newsletter
Read articles from Amelia Dutta directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Amelia Dutta
Amelia Dutta
Front-End Web Developer | React, JavaScript, HTML, CSS | AWS Certified | Creating Engaging User Experiences