Cauldron Simulator
This prompt requests code for an interactive web-based cauldron simulator. Users can drag blocks to change liquid temperature, triggering visual changes, animations, and comments, with a reset option.
Prompt
I am a teacher and want to create an interactive web app for my lessons. I want a cauldron simulator in which:
- The cauldron shows the current temperature.
- A 'liquid' in the cauldron changes visually (in height and color) based on the temperature.
- Two blocks are present: one for increasing (plus) and one for decreasing (minus) the temperature. You can drag these blocks to the cauldron to adjust the temperature.
- When dragging, animations (emojis) appear and a funny comment is shown depending on the temperature.
- There is also a reset button to reset the simulator to the starting values.
Can you provide a web app code to build this?Example Output
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>π§ Cauldron Simulator π‘οΈ</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
background: #00ae26;
margin: 20px;
}
h1 {
margin-bottom: 20px;
color: #ffffff; /* Purple */
}
/* Central simulator container */
.simulation-container {
max-width: 400px;
margin: 0 auto;
}
/* Square cauldron (dropzone) */
.cauldron {
position: relative;
width: 250px;
height: 250px;
background: #555;
border: 5px solid #222;
margin: 0 auto;
}
/* The "liquid" in the cauldron with color transition */
.liquid {
position: absolute;
bottom: 0;
width: 100%;
height: 0%;
background-color: blue;
transition: height 0.5s, background-color 0.5s;
}
/* Temperature text in the cauldron */
.temp-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
font-size: 28px;
font-weight: bold;
text-shadow: 1px 1px 2px #000;
pointer-events: none;
}
/* Container for the two temperature blocks */
.blocks-container {
display: flex;
justify-content: space-around;
margin: 20px 0;
}
/* Block styling */
.block {
padding: 15px;
border-radius: 5px;
display: flex;
align-items: center;
gap: 5px;
cursor: grab;
}
/* Plus block (left, red) */
#blockPlus {
background: red;
color: #fff;
}
/* Minus block (right, blue) */
#blockMinus {
background: blue;
color: #fff;
}
/* Icons: make them larger */
.icon {
font-size: 28px;
font-weight: bold;
}
/* Ensure the input field does not drag */
.block input {
font-size: 24px;
width: 80px;
text-align: center;
border: none;
background: transparent;
color: inherit;
/* Prevent the input field from triggering a dragstart */
pointer-events: auto;
}
/* Funny comments below the cauldron: background becomes purple, text green */
.joke-container {
background: #800080; /* Purple */
color: #00ff00; /* Green letters */
border: 2px dashed #e0c97f;
border-radius: 10px;
padding: 15px;
margin-top: 20px;
text-align: center;
min-height: 50px;
}
/* Reset button */
.reset-btn {
margin-top: 20px;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
/* Emoji animation: multiple at once, visible longer */
.emoji-fly {
position: absolute;
font-size: 32px;
pointer-events: none;
animation: flyOut 2s forwards;
}
@keyframes flyOut {
0% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(-150px);
}
}
</style>
</head>
<body>
<h1>π§ Cauldron Simulator π‘οΈ</h1>
<div class="simulation-container">
<div
id="cauldron"
class="cauldron"
ondrop="drop(event)"
ondragover="allowDrop(event)"
>
<div id="liquid" class="liquid"></div>
<div id="tempText" class="temp-text">-5Β°C</div>
</div>
<div class="blocks-container">
<div
class="block"
id="blockPlus"
draggable="true"
ondragstart="drag(event)"
>
<span class="icon">+</span>
<input
type="number"
value="0"
step="0.5"
ondragstart="event.stopPropagation()"
/>
</div>
<div
class="block"
id="blockMinus"
draggable="true"
ondragstart="drag(event)"
>
<span class="icon">-</span>
<input
type="number"
value="0"
step="0.5"
ondragstart="event.stopPropagation()"
/>
</div>
</div>
<div class="joke-container">
<h2>Magic Temperature π§ββοΈ</h2>
<div id="joke"></div>
</div>
<button class="reset-btn" onclick="resetSimulator()">
Reset Simulator π
</button>
</div>
<script>
// Initial values
let currentTemp = -5;
const minTemp = -10;
const maxTemp = 20;
// Elements
const tempText = document.getElementById('tempText');
const liquid = document.getElementById('liquid');
const cauldron = document.getElementById('cauldron');
const jokeDiv = document.getElementById('joke');
// Update the display in the cauldron
function updateDisplay() {
tempText.textContent = currentTemp + 'Β°C';
// Calculate fill percentage based on the range
const clampedTemp = Math.min(Math.max(currentTemp, minTemp), maxTemp);
const percent = ((clampedTemp - minTemp) / (maxTemp - minTemp)) * 100;
liquid.style.height = percent + '%';
// Color transition: below 0 blue, from 0 red
liquid.style.backgroundColor = currentTemp < 0 ? 'blue' : 'red';
updateJoke();
}
// Update the funny comment below the cauldron
function updateJoke() {
let joke = '';
if (currentTemp < 0) {
joke = 'Brrr... Ice cold! π₯ΆβοΈ';
} else if (currentTemp < 10) {
joke = 'It\'s starting to warm up... ππ€οΈ';
} else if (currentTemp < 20) {
joke = 'Sunny vibes in the cauldron! ππ';
} else {
joke = 'Wow, hot cauldron! π₯π₯΅';
}
jokeDiv.textContent = joke;
}
// Ensure the dropzone (cauldron) accepts drops
function allowDrop(ev) {
ev.preventDefault();
}
// On dragstart: get the value and set it in DataTransfer
function drag(ev) {
const block = ev.target.closest('.block');
const input = block.querySelector('input');
let value = parseFloat(input.value) || 0;
// Determine if it's the plus or minus block (based on id)
const blockType = block.id === 'blockPlus' ? 'plus' : 'minus';
// Ensure plus block always gives a positive value and minus block a negative
value = blockType === 'plus' ? Math.abs(value) : -Math.abs(value);
ev.dataTransfer.setData('value', value);
ev.dataTransfer.setData('blockId', block.id);
}
// On drop in the cauldron
function drop(ev) {
ev.preventDefault();
let value = parseFloat(ev.dataTransfer.getData('value')) || 0;
const blockId = ev.dataTransfer.getData('blockId');
currentTemp += value;
updateDisplay();
animateEmojis();
// Reset the input field of the dragged block to 0
document.querySelector(`#${blockId} input`).value = 0;
}
// Make multiple emojis "fly" out of the cauldron simultaneously
function animateEmojis() {
let emoji = '';
if (currentTemp < 0) {
emoji = 'βοΈ';
} else if (currentTemp < 10) {
emoji = 'π€οΈ';
} else if (currentTemp < 20) {
emoji = 'π';
} else {
emoji = 'π₯';
}
// Create 3 emojis with random horizontal positions
for (let i = 0; i < 3; i++) {
let span = document.createElement('span');
span.className = 'emoji-fly';
span.textContent = emoji;
// Random left position within the cauldron (between 10% and 80%)
span.style.left = Math.random() * 70 + 10 + '%';
span.style.top = '20%';
cauldron.appendChild(span);
span.addEventListener('animationend', () => {
cauldron.removeChild(span);
});
}
}
// Reset the simulator
function resetSimulator() {
currentTemp = -5;
updateDisplay();
document.querySelector('#blockPlus input').value = 0;
document.querySelector('#blockMinus input').value = 0;
}
// Initialization
updateDisplay();
</script>
</body>
</html>