Day 890

Chep
6 min readAug 29, 2024

--

Meant to write a story, but then I got busy playing around with Replit and AI tools:

https://x.com/ConorChepenik/status/1828992040367014011

You can find the app here — However, if I’ve stopped paying for Replit and the app is no longer hosted when you’re reading this, I’ve included some screenshots below as well as the HTML, CSS, & JS code.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Epic Shape Generator</title>
<meta name="description" content="Create and animate colorful shapes with the Epic Shape Generator. Unleash your creativity with interactive controls and stunning animations.">
<meta name="keywords" content="shape generator, animation, interactive, creative tool, web app">
<meta name="author" content="Your Name">
<meta property="og:title" content="Epic Shape Generator">
<meta property="og:description" content="Create and animate colorful shapes with the Epic Shape Generator. Unleash your creativity with interactive controls and stunning animations.">
<meta property="og:image" content="https://images.unsplash.com/photo-1550745165-9bc0b252726f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1200&h=630&q=80">
<meta property="og:url" content="https://epicshapegenerator.replit.app/">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Epic Shape Generator">
<meta name="twitter:description" content="Create and animate colorful shapes with the Epic Shape Generator. Unleash your creativity with interactive controls and stunning animations.">
<meta name="twitter:image" content="https://images.unsplash.com/photo-1550745165-9bc0b252726f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1200&h=630&q=80">
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #1a1a2e;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
overflow: hidden;
}
.container {
background-color: #16213e;
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
display: flex;
flex-direction: column;
align-items: center;
width: 90%;
max-width: 600px;
}
#canvas {
background-color: #0f3460;
border-radius: 10px;
margin-bottom: 20px;
}
.controls {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
margin-bottom: 20px;
width: 100%;
}
button {
background-color: #e94560;
color: white;
border: none;
padding: 10px 20px;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 14px;
font-weight: bold;
text-transform: uppercase;
flex: 1;
min-width: 120px;
}
button:hover {
background-color: #ff6b6b;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(233, 69, 96, 0.4);
}
.color-controls {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 20px;
}
input[type="color"] {
-webkit-appearance: none;
width: 40px;
height: 40px;
border: none;
border-radius: 50%;
background: none;
cursor: pointer;
padding: 0;
}
input[type="color"]::-webkit-color-swatch-wrapper {
padding: 0;
}
input[type="color"]::-webkit-color-swatch {
border: none;
border-radius: 50%;
box-shadow: 0 0 0 2px white;
}
.slider-container {
width: 100%;
margin-bottom: 10px;
}
.slider-label {
display: flex;
justify-content: space-between;
margin-bottom: 5px;
}
input[type="range"] {
width: 100%;
-webkit-appearance: none;
background: #e94560;
outline: none;
border-radius: 15px;
height: 10px;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
background: #fff;
cursor: pointer;
border-radius: 50%;
}
</style>
</head>
<body>
<div class="container">
<h1>Epic Shape Generator</h1>
<canvas id="canvas" width="500" height="300"></canvas>
<div class="controls">
<button id="changeShapeBtn">Change Shape</button>
<button id="randomColorBtn">Random Color</button>
<button id="animateBtn">Animate</button>
<button id="toggleMultipleBtn">Toggle Multiple</button>
</div>
<div class="color-controls">
<input type="color" id="colorPicker" value="#e94560">
<span id="colorDisplay">#e94560</span>
</div>
<div class="slider-container">
<div class="slider-label">
<span>Size:</span>
<span id="sizeValue">100</span>
</div>
<input type="range" id="sizeSlider" min="10" max="200" value="100">
</div>
<div class="slider-container">
<div class="slider-label">
<span>Rotation:</span>
<span id="rotationValue">0°</span>
</div>
<input type="range" id="rotationSlider" min="0" max="360" value="0">
</div>
</div>

<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const changeShapeBtn = document.getElementById('changeShapeBtn');
const randomColorBtn = document.getElementById('randomColorBtn');
const colorPicker = document.getElementById('colorPicker');
const colorDisplay = document.getElementById('colorDisplay');
const animateBtn = document.getElementById('animateBtn');
const toggleMultipleBtn = document.getElementById('toggleMultipleBtn');
const sizeSlider = document.getElementById('sizeSlider');
const rotationSlider = document.getElementById('rotationSlider');
const sizeValue = document.getElementById('sizeValue');
const rotationValue = document.getElementById('rotationValue');

let currentShape = 'circle';
let currentColor = '#e94560';
let isMultiple = false;
let size = 100;
let rotation = 0;
let isAnimating = false;
let animationId;

const shapes = ['circle', 'square', 'triangle', 'star', 'heart'];

function drawShape(x, y, shape, color, size, rotation) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(rotation * Math.PI / 180);
ctx.fillStyle = color;

switch(shape) {
case 'circle':
ctx.beginPath();
ctx.arc(0, 0, size / 2, 0, 2 * Math.PI);
ctx.fill();
break;
case 'square':
ctx.fillRect(-size / 2, -size / 2, size, size);
break;
case 'triangle':
ctx.beginPath();
ctx.moveTo(0, -size / 2);
ctx.lineTo(-size / 2, size / 2);
ctx.lineTo(size / 2, size / 2);
ctx.closePath();
ctx.fill();
break;
case 'star':
let spikes = 5;
let outerRadius = size / 2;
let innerRadius = size / 4;
ctx.beginPath();
for(let i = 0; i < spikes * 2; i++) {
let radius = i % 2 === 0 ? outerRadius : innerRadius;
let angle = i * Math.PI / spikes;
ctx.lineTo(Math.cos(angle) * radius, Math.sin(angle) * radius);
}
ctx.closePath();
ctx.fill();
break;
case 'heart':
ctx.beginPath();
ctx.moveTo(0, size / 4);
ctx.bezierCurveTo(size / 4, 0, size / 2, size / 4, 0, size / 2);
ctx.bezierCurveTo(-size / 2, size / 4, -size / 4, 0, 0, size / 4);
ctx.fill();
break;
}
ctx.restore();
}

function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (isMultiple) {
for (let i = 0; i < 5; i++) {
let x = Math.random() * canvas.width;
let y = Math.random() * canvas.height;
let shape = shapes[Math.floor(Math.random() * shapes.length)];
let color = `hsl(${Math.random() * 360}, 100%, 50%)`;
let size = Math.random() * 100 + 50;
let rotation = Math.random() * 360;
drawShape(x, y, shape, color, size, rotation);
}
} else {
drawShape(canvas.width / 2, canvas.height / 2, currentShape, currentColor, size, rotation);
}
}

function animate() {
if (!isAnimating) return;

rotation = (rotation + 2) % 360;
size = 100 + Math.sin(Date.now() / 500) * 50;

rotationSlider.value = rotation;
sizeSlider.value = size;
updateSliderValues();
draw();
animationId = requestAnimationFrame(animate);
}

function updateSliderValues() {
sizeValue.textContent = Math.round(size);
rotationValue.textContent = Math.round(rotation) + '°';
}

changeShapeBtn.addEventListener('click', () => {
const currentIndex = shapes.indexOf(currentShape);
currentShape = shapes[(currentIndex + 1) % shapes.length];
draw();
});

randomColorBtn.addEventListener('click', () => {
currentColor = '#' + Math.floor(Math.random()*16777215).toString(16);
colorPicker.value = currentColor;
colorDisplay.textContent = currentColor;
draw();
});

colorPicker.addEventListener('input', (e) => {
currentColor = e.target.value;
colorDisplay.textContent = currentColor;
draw();
});

animateBtn.addEventListener('click', () => {
isAnimating = !isAnimating;
if (isAnimating) {
animate();
animateBtn.textContent = 'Stop';
} else {
cancelAnimationFrame(animationId);
animateBtn.textContent = 'Animate';
}
});

toggleMultipleBtn.addEventListener('click', () => {
isMultiple = !isMultiple;
draw();
});

sizeSlider.addEventListener('input', (e) => {
size = parseInt(e.target.value);
updateSliderValues();
draw();
});

rotationSlider.addEventListener('input', (e) => {
rotation = parseInt(e.target.value);
updateSliderValues();
draw();
});

draw();
</script>
</body>
</html>

I had planned to write an article for Bitcoinnews tonight, but I found this diversion quite enjoyable. While this app might not win any awards, I believe there’s immense value in building things purely for play and experimentation. Life is more fun that way.

I’m currently reading about Claude Shannon, who seemed to embody this philosophy. His playful approach to problem-solving helped transform the world from analog to digital — a shift I consider largely positive. It’s inspiring to see how curiosity-driven tinkering can lead to groundbreaking innovations.

https://x.com/ConorChepenik/status/1828984778164461891

Balancing the analog and digital worlds is a challenge many of us face. While there’s undeniable charm and nostalgia in physical objects, the efficiency and innovation of digital technologies are reshaping our world in profound ways. Bitcoin exemplifies this shift, offering a digital solution to age-old monetary challenges. As we navigate this evolving landscape, it’s crucial to appreciate the strengths of both realms. The key lies not in wholly abandoning one for the other, but in thoughtfully integrating digital advancements while preserving the meaningful aspects of our analog heritage. This balance allows us to harness the best of both worlds, creating a future that’s both innovative and grounded in human experience.

8/28/24

Conor Jay Chepenik

--

--

Chep
Chep

Written by Chep

I've decided to write everyday for the rest of my life or until Medium goes out of business.

No responses yet