If you have been using ChatGPT, you may have already seen this feature there, but Gemini has added a “canvas” to it’s potential workflow. The canvas is a separate “frame” or space on the page that gets updated as you work on whatever is in it with Gemini, without it scrolling up and away as you progress. I’ve mostly used it for things like code, but you can totally use it for working on a draft of a regular written object, like an essay, speech, screenplay, etc.
To activate it, you will want to open https://gemini.google.com/app.
Once there tell it what you want to work on – I’ll think of something on the simple side. Here is my prompt:
I would like to write a small app that lets me know that everything will be alright. I want it to suggest a nice quote and also ask me if I want to hear some soothing music. Can you help me write that?
Then, I want to use the new canvas feature. This requires that you click the button in the chat/prompt window that says “canvas” Now my screen looks like this:
It even has a preview mode!
I’m going to ask it to change how the music is selected. At this stage it is asking me to link it to the music from my own drive. I want it to find it on the internet for me.
Can you change it so that you find the music on the internet for me? Tell me what you selected and ask if I want to hear it?
It created some code that uses an API to retrieve the music, and because I don’t feel like fussing with that I have it rewrite the code one more time to play music without the need of a API. It tells me I really should use the API, but gives me what I wanted anyway. lol This is the result:
preview: (the preview is fully functional)
It doesn’t sound like Calming ocean waves” but it is fun music. Eventually I got sick of the synthy fun and asked it to find more appropriate music. Here is the code. You can copy it into a text/notepad app and save it as a html file if you want to play it in the browser on your computer
https://drive.google.com/file/d/1HtllluclKKzucMunkwVQ6kpSAaxLBtYr/view?usp=sharing
There is another feature “audio overview”. If you’ve used NotebookLM, I’m sure you’ve already seen it. They are bringing it to regular Gemini, but I don’t personally see it yet, so I believe it isn’t fully rolled out yet.
Have fun!
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<title>Everything Will Be Alright</title>
<link href=”https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap” rel=”stylesheet”>
<style>
body {
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f0f0f0;
font-family: ‘Inter’, sans-serif;
overflow: hidden; /* Prevent scrollbars during animation */
}
#canvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.container {
background-color: rgba(255, 255, 255, 0.8); /* Semi-transparent white */
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
max-width: 80%;
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.3);
}
h1 {
color: #333;
margin-bottom: 20px;
font-size: 2em;
}
p {
color: #555;
font-size: 1.1em;
line-height: 1.6;
margin-bottom: 25px;
}
button {
padding: 12px 25px;
border: none;
border-radius: 6px;
background-color: #4CAF50; /* Green */
color: white;
font-size: 1.1em;
cursor: pointer;
transition: background-color 0.3s ease;
margin-top: 10px;
font-family: ‘Inter’, sans-serif;
}
button:hover {
background-color: #367c39; /* Darker green */
}
#music-container {
display: flex;
align-items: center;
justify-content: center;
margin-top: 20px;
flex-direction: column; /* Stack items vertically */
}
#play-music {
padding: 12px 25px;
border: none;
border-radius: 6px;
background-color: #007BFF; /* Blue */
color: white;
font-size: 1.1em;
cursor: pointer;
transition: background-color 0.3s ease;
margin-top: 10px;
font-family: ‘Inter’, sans-serif;
}
#play-music:hover {
background-color: #0056b3; /* Darker blue */
}
#music-controls {
display: none; /* Initially hide the controls */
flex-direction: column;
align-items: center;
margin-top: 10px;
background-color: rgba(255,255,255,0.7);
padding: 10px;
border-radius: 8px;
border: 1px solid rgba(255,255,255,0.3);
backdrop-filter: blur(5px);
}
#music-controls button {
margin: 5px;
padding: 8px 16px;
border: none;
border-radius: 6px;
background-color: #4CAF50; /* Green */
color: white;
font-size: 0.9em;
cursor: pointer;
transition: background-color 0.3s ease;
font-family: ‘Inter’, sans-serif;
}
#music-controls button:hover {
background-color: #367c39; /* Darker green */
}
#volume-control {
width: 100px;
margin: 10px 0;
}
#music-info {
margin-bottom: 10px;
font-size: 0.9em;
color: #555;
}
</style>
</head>
<body>
<canvas id=”canvas”></canvas>
<div class=”container”>
<h1>Everything Will Be Alright</h1>
<p id=”quote”>Loading a reassuring quote…</p>
<div id=”music-container”>
<p id=”music-info”>Finding soothing music…</p>
<button id=”play-music” style=”display:none;”>Play Soothing Music</button>
</div>
<div id=”music-controls”>
<button id=”pause-music”>Pause</button>
<button id=”resume-music”>Resume</button>
<label for=”volume-control”>Volume:</label>
<input type=”range” id=”volume-control” min=”0″ max=”1″ step=”0.01″>
</div>
</div>
<script>
const canvas = document.getElementById(‘canvas’);
const ctx = canvas.getContext(‘2d’);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let stars = [];
const numStars = 200;
function initializeStars() {
stars = [];
for (let i = 0; i < numStars; i++) {
stars.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
radius: Math.random() * 2,
color: `rgba(255, 255, 255, ${Math.random() * 0.5 + 0.5})`, // Varying opacity
speedX: (Math.random() – 0.5) * 0.2, // Subtle movement
speedY: (Math.random() – 0.5) * 0.2
});
}
}
function drawStars() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
stars.forEach(star => {
ctx.beginPath();
ctx.arc(star.x, star.y, star.radius, 0, Math.PI * 2);
ctx.fillStyle = star.color;
ctx.fill();
ctx.closePath();
// Update star position for subtle animation
star.x += star.speedX;
star.y += star.speedY;
// Wrap around edges
if (star.x > canvas.width) star.x = 0;
if (star.x < 0) star.x = canvas.width;
if (star.y > canvas.height) star.y = 0;
if (star.y < 0) star.y = canvas.height;
});
}
function animateStars() {
drawStars();
requestAnimationFrame(animateStars);
}
initializeStars();
animateStars();
window.addEventListener(‘resize’, () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
initializeStars(); // Re-initialize stars on resize for proper distribution
});
const quoteElement = document.getElementById(‘quote’);
const quotes = [
“This too shall pass.”,
“Every cloud has a silver lining.”,
“You are stronger than you think.”,
“The best is yet to come.”,
“Breathe. You’re going to be okay. Breathe and remember that you’ve been in this place before. You’ve been this uncomfortable and anxious and scared, and you’ve survived. Breathe and know that you can survive this too. These feelings can’t break you. They’re painful and exhausting, but they can’t break you. You’re going to be okay.”
];
function displayQuote() {
const randomQuote = quotes[Math.floor(Math.random() * quotes.length)];
quoteElement.textContent = randomQuote;
}
displayQuote();
setInterval(displayQuote, 10000); // Change quote every 10 seconds
const playMusicButton = document.getElementById(‘play-music’);
const audio = new Audio();
const musicControls = document.getElementById(‘music-controls’);
const pauseMusicButton = document.getElementById(‘pause-music’);
const resumeMusicButton = document.getElementById(‘resume-music’);
const volumeControl = document.getElementById(‘volume-control’);
const musicInfo = document.getElementById(‘music-info’);
let isPlaying = false;
let selectedTrack = null; // Store the selected track URL
// Simulate an online search for music without an API
function searchForSoothingMusic() {
const tracks = [
{ title: “Relaxing Piano Music”, url: “https://www.bensound.com/royalty-free-music/track/rainy-day-lo-fi-jazz” },
{ title: “Gentle Rain Sounds”, url: “https://www.soundjay.com/buttons/sounds/rain-01a.mp3” },
{ title: “Calming Ocean Waves”, url: “https://www.soundjay.com/buttons/sounds/ocean-wave-01.mp3” },
{ title: “Soft Ambient Music”, url: “https://www.bensound.com/bensound-music/bensound-acousticbreeze.mp3” },
{ title: “Peaceful Flute Music”, url: “https://www.soundjay.com/buttons/sounds/flute-c-note.mp3” },
];
setTimeout(() => {
selectedTrack = tracks[Math.floor(Math.random() * tracks.length)];
musicInfo.textContent = `Found: “${selectedTrack.title}”. Would you like to hear it?`;
playMusicButton.textContent = “Play Music”;
playMusicButton.style.display = ‘inline-block’;
}, 2000);
}
searchForSoothingMusic();
playMusicButton.addEventListener(‘click’, () => {
if (selectedTrack) {
audio.src = selectedTrack.url;
musicControls.style.display = ‘flex’;
if (isPlaying) {
audio.pause();
isPlaying = false;
playMusicButton.textContent = “Resume Music”;
} else {
audio.play().then(() => {
isPlaying = true;
playMusicButton.textContent = “Pause Music”;
}).catch(error => {
console.error(“Playback failed:”, error);
alert(“Failed to play music. There may be an issue with the selected audio source.”);
isPlaying = false;
playMusicButton.textContent = “Play Music”;
});
}
} else {
alert(“No music track selected.”);
}
});
pauseMusicButton.addEventListener(‘click’, () => {
if (isPlaying) {
audio.pause();
isPlaying = false;
playMusicButton.textContent = “Resume Music”;
}
});
resumeMusicButton.addEventListener(‘click’, () => {
if (!isPlaying) {
audio.play().then(() => {
isPlaying = true;
playMusicButton.textContent = “Pause Music”;
}).catch(error => {
console.error(“Playback failed:”, error);
alert(“Failed to resume music.”);
isPlaying = false;
playMusicButton.textContent = “Play Music”;
});
}
});
volumeControl.addEventListener(‘input’, () => {
audio.volume = volumeControl.value;
});
audio.addEventListener(‘ended’, () => {
isPlaying = false;
playMusicButton.textContent = “Play Music”;
});
</script>
</body>
</html>