Model: openrouter/moonshotai/kimi-k2.6
Generate an SVG of a pelican riding a bicycle
Here is a complete, interactive HTML application featuring a vector-drawn Pelican on a bicycle.
This solution uses SVG for high-quality rendering and JavaScript (via requestAnimationFrame) for all animations.
- Copy the code block below.
- Save it as an
.htmlfile (e.g.,pelican.html). - Open it in any modern web browser.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pedaling Pelican</title>
<style>
:root {
--bg-color: #2c3e50;
--panel-color: #ecf0f1;
--accent-color: #e67e22;
--text-color: #2c3e50;
}
body {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: var(--bg-color);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
overflow: hidden;
}
.canvas-container {
position: relative;
width: 90%;
max-width: 800px;
background: linear-gradient(to bottom, #87CEEB 0%, #E0F7FA 60%, #dcdcdc 60%, #95a5a6 100%);
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
overflow: hidden;
border: 4px solid #34495e;
}
/* Control Panel Styling - OUTSIDE SVG */
.controls {
margin-top: 20px;
padding: 15px 25px;
background-color: var(--panel-color);
border-radius: 50px;
display: flex;
gap: 20px;
align-items: center;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.control-group {
display: flex;
flex-direction: column;
align-items: center;
}
label {
font-size: 0.8rem;
font-weight: bold;
color: var(--text-color);
margin-bottom: 5px;
text-transform: uppercase;
letter-spacing: 1px;
}
input[type=range] {
-webkit-appearance: none;
width: 150px;
height: 6px;
background: #bdc3c7;
border-radius: 5px;
outline: none;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--accent-color);
cursor: pointer;
transition: transform 0.1s;
}
input[type=range]::-webkit-slider-thumb:hover {
transform: scale(1.2);
}
button {
background-color: var(--accent-color);
color: white;
border: none;
padding: 10px 20px;
border-radius: 20px;
font-weight: bold;
cursor: pointer;
font-size: 1rem;
transition: background 0.2s, transform 0.1s;
}
button:hover {
background-color: #d35400;
}
button:active {
transform: scale(0.95);
}
/* Scenery CSS Animation (Allowed as it's not SVG) */
.cloud {
position: absolute;
background: rgba(255, 255, 255, 0.8);
border-radius: 50px;
animation: floatCloud 20s linear infinite;
}
@keyframes floatCloud {
0% { transform: translateX(120%); }
100% { transform: translateX(-120%); }
}
</style>
</head>
<body>
<div class="canvas-container">
<!-- Scenery Elements (HTML/CSS for background movement) -->
<div class="cloud" style="width: 100px; height: 40px; top: 10%; left: 20%; animation-duration: 25s;"></div>
<div class="cloud" style="width: 140px; height: 60px; top: 15%; left: 60%; animation-duration: 18s;"></div>
<svg id="mainSvg" viewBox="0 0 800 500" preserveAspectRatio="xMidYMid slice" style="width: 100%; height: auto; display: block;">
<defs>
<!-- Gradients -->
<linearGradient id="beakGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#f39c12;stop-opacity:1" />
<stop offset="100%" style="stop-color:#e67e22;stop-opacity:1" />
</linearGradient>
<linearGradient id="metalGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#bdc3c7;stop-opacity:1" />
<stop offset="50%" style="stop-color:#95a5a6;stop-opacity:1" />
<stop offset="100%" style="stop-color:#7f8c8d;stop-opacity:1" />
</linearGradient>
<filter id="shadowBlur">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
</defs>
<!-- Ground Shadow -->
<ellipse cx="400" cy="440" rx="180" ry="15" fill="rgba(0,0,0,0.2)" filter="url(#shadowBlur)" />
<!-- === THE BIKE === -->
<g id="bike" transform="translate(0, 0)">
<!-- Back Wheel -->
<g id="wheelBack" transform="translate(250, 400)">
<circle r="60" fill="none" stroke="#34495e" stroke-width="8" />
<circle r="55" fill="none" stroke="#ecf0f1" stroke-width="4" />
<!-- Spokes Group -->
<g id="spokesBack">
<line x1="0" y1="-55" x2="0" y2="55" stroke="#bdc3c7" stroke-width="2" />
<line x1="-48" y1="-27" x2="48" y2="27" stroke="#bdc3c7" stroke-width="2" />
<line x1="-48" y1="27" x2="48" y2="-27" stroke="#bdc3c7" stroke-width="2" />
</g>
<circle r="8" fill="#7f8c8d" />
</g>
<!-- Front Wheel -->
<g id="wheelFront" transform="translate(550, 400)">
<circle r="60" fill="none" stroke="#34495e" stroke-width="8" />
<circle r="55" fill="none" stroke="#ecf0f1" stroke-width="4" />
<g id="spokesFront">
<line x1="0" y1="-55" x2="0" y2="55" stroke="#bdc3c7" stroke-width="2" />
<line x1="-48" y1="-27" x2="48" y2="27" stroke="#bdc3c7" stroke-width="2" />
<line x1="-48" y1="27" x2="48" y2="-27" stroke="#bdc3c7" stroke-width="2" />
</g>
<circle r="8" fill="#7f8c8d" />
</g>
<!-- Frame -->
<path d="M250 400 L360 400 L310 300 L250 400" fill="none" stroke="#e74c3c" stroke-width="8" stroke-linejoin="round" />
<path d="M310 300 L500 300 L550 400" fill="none" stroke="#e74c3c" stroke-width="8" stroke-linecap="round" />
<path d="M360 400 L450 250" fill="none" stroke="#e74c3c" stroke-width="6" stroke-linecap="round" /> <!-- Seat post -->
<path d="M500 300 L490 220" fill="none" stroke="#e74c3c" stroke-width="6" stroke-linecap="round" /> <!-- Handlebar post -->
<!-- Chain/Gear -->
<circle cx="360" cy="400" r="15" fill="none" stroke="#7f8c8d" stroke-width="3" />
<circle cx="360" cy="400" r="5" fill="#2c3e50" />
<path d="M360 400 L250 400" stroke="#7f8c8d" stroke-width="3" stroke-dasharray="4,2" /> <!-- Chain -->
<!-- Pedal Crank (Left Side - Visual) -->
<g id="pedalCrank" transform="translate(360, 400)">
<line x1="0" y1="0" x2="0" y2="30" stroke="#7f8c8d" stroke-width="5" stroke-linecap="round" />
<rect x="-8" y="25" width="16" height="10" rx="2" fill="#2c3e50" />
</g>
<!-- Handlebars -->
<path d="M490 220 L480 200 L510 200" fill="none" stroke="#bdc3c7" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" />
<circle cx="510" cy="200" r="5" fill="#2c3e50" /> <!-- Grip -->
<!-- Seat -->
<path d="M420 250 Q460 245 470 255" fill="none" stroke="#2c3e50" stroke-width="12" stroke-linecap="round" />
</g>
<!-- === THE PELICAN === -->
<g id="pelican" transform="translate(0, -20)">
<!-- Back Leg (Left Leg) -->
<g id="legLeft" transform="translate(440, 320)">
<path d="M0 0 L-10 40" stroke="#f1c40f" stroke-width="8" stroke-linecap="round" />
<path d="M-10 40 L-20 50 L0 50" fill="none" stroke="#f1c40f" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" />
</g>
<!-- Body -->
<ellipse cx="420" cy="280" rx="70" ry="50" fill="#ffffff" />
<path d="M380 260 Q420 240 460 270" fill="none" stroke="#ecf0f1" stroke-width="2" opacity="0.5"/>
<!-- Neck & Head -->
<g id="headGroup" transform="translate(460, 240) rotate(-10)">
<path d="M0 40 Q10 0 30 -20 Q60 -40 90 -20" fill="none" stroke="#ffffff" stroke-width="28" stroke-linecap="round" />
<!-- Head Detail -->
<circle cx="85" cy="-25" r="16" fill="#ffffff" />
<!-- Beak -->
<path d="M90 -25 L160 -10 L90 5 Z" fill="url(#beakGradient)" stroke="#d35400" stroke-width="1" />
<path d="M90 5 Q110 20 120 25 Q100 25 90 5" fill="#e67e22" /> <!-- Pouch -->
<!-- Eye -->
<circle cx="80" cy="-30" r="3" fill="#000" />
</g>
<!-- Wing (Flapping) -->
<g id="wing" transform="translate(420, 270)">
<path d="M0 0 Q-40 -10 -60 20 Q-20 40 10 20 Z" fill="#ffffff" stroke="#bdc3c7" stroke-width="2" />
<!-- Wing Feathers -->
<path d="M-60 20 L-50 35 M-45 23 L-35 38 M-30 25 L-20 40" stroke="#bdc3c7" stroke-width="2" stroke-linecap="round"/>
</g>
<!-- Front Leg (Right Leg - Pushing Pedal) -->
<g id="legRightGroup" transform="translate(440, 320)">
<line id="thigh" x1="0" y1="0" x2="10" y2="40" stroke="#f1c40f" stroke-width="8" stroke-linecap="round" />
<g id="shinGroup" transform="translate(10, 40)">
<line id="shin" x1="0" y1="0" x2="-5" y2="40" stroke="#f1c40f" stroke-width="7" stroke-linecap="round" />
<g id="footGroup" transform="translate(-5, 40)">
<path d="M0 0 L-10 10 L10 10" fill="none" stroke="#f1c40f" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" />
</g>
</g>
</g>
</g>
</svg>
</div>
<!-- Control Panel -->
<div class="controls">
<button id="toggleBtn">Pause</button>
<div class="control-group">
<label for="speedRange">Speed</label>
<input type="range" id="speedRange" min="0" max="20" value="8">
</div>
<div class="control-group">
<label for="flapRange">Wing Flap</label>
<input type="range" id="flapRange" min="0" max="10" value="5">
</div>
</div>
<script>
// === Configuration & State ===
const state = {
isPlaying: true,
speed: 8, // Pedal speed
flapIntensity: 5, // Wing movement
wheelAngle: 0,
pedalAngle: 0,
wingAngle: 0,
time: 0
};
// === DOM Elements ===
const els = {
wheelBack: document.getElementById('wheelBack'),
wheelFront: document.getElementById('wheelFront'),
pedalCrank: document.getElementById('pedalCrank'),
legRightGroup: document.getElementById('legRightGroup'),
thigh: document.getElementById('thigh'),
shin: document.getElementById('shin'),
shinGroup: document.getElementById('shinGroup'),
footGroup: document.getElementById('footGroup'),
wing: document.getElementById('wing'),
head: document.getElementById('headGroup'),
// Controls
toggleBtn: document.getElementById('toggleBtn'),
speedRange: document.getElementById('speedRange'),
flapRange: document.getElementById('flapRange')
};
// === Animation Logic ===
function toRad(deg) {
return deg * (Math.PI / 180);
}
function updatePhysics() {
if (!state.isPlaying) return;
// Increment Time
state.time += state.speed * 0.05;
// 1. Wheels (Simple Rotation)
state.wheelAngle += state.speed * 2;
const wheelTransform = `rotate(${state.wheelAngle})`;
els.wheelBack.setAttribute('transform', `translate(250, 400) ${wheelTransform}`);
els.wheelFront.setAttribute('transform', `translate(550, 400) ${wheelTransform}`);
// 2. Pedals (Circular Motion)
state.pedalAngle += state.speed * 2;
const pedalRad = toRad(state.pedalAngle);
const crankX = Math.cos(pedalRad) * 30; // Crank length approx 30
const crankY = Math.sin(pedalRad) * 30;
els.pedalCrank.setAttribute('transform', `translate(360, 400) rotate(${state.pedalAngle})`);
// 3. Inverse Kinematics (Simplified) for Right Leg
// Hip is at (440, 300) relative to group, but group is translated to 440, 320
// Let's assume Hip is origin of legRightGroup (0,0)
// Target is Pedal Center + Crank Offset
// Global Coordinates of Pedal Center
const pedalCenterX = 360;
const pedalCenterY = 400;
// Current Pedal Position
const targetX = crankX; // Relative to pedal center
const targetY = crankY; // Relative to pedal center
// We need to map this relative to the Hip (440, 320)
// Hip Offset from Pedal Center: Hip(440,320) - Pedal(360,400) = (80, -80)
const hipOffsetX = 80;
const hipOffsetY = -80;
const localTargetX = targetX - hipOffsetX;
const localTargetY = targetY - hipOffsetY;
// Inverse Kinematics for 2-segment leg
// Segment lengths: Thigh (L1) approx 45, Shin (L2) approx 45
const L1 = 45;
const L2 = 45;
const dist = Math.sqrt(localTargetX**2 + localTargetY**2);
// Clamp distance to avoid breaking
const clampedDist = Math.min(dist, L1 + L2 - 0.1);
// Law of Cosines
// c^2 = a^2 + b^2 - 2ab cos(C)
// Angle at knee (internal)
const cosKnee = (L1**2 + L2**2 - clampedDist**2) / (2 * L1 * L2);
let kneeAngle = Math.acos(Math.max(-1, Math.min(1, cosKnee))); // 0 to PI
// Angle of the vector to target
const targetAngle = Math.atan2(localTargetY, localTargetX);
// Angle of thigh relative to horizontal
// Law of Cosines for thigh angle relative to target line
const cosThighOffset = (L1**2 + clampedDist**2 - L2**2) / (2 * L1 * clampedDist);
const thighOffset = Math.acos(Math.max(-1, Math.min(1, cosThighOffset)));
const thighAngle = targetAngle + thighOffset;
// Apply Transforms to Leg
// Thigh rotation
els.thigh.setAttribute('x2', Math.cos(thighAngle) * L1);
els.thigh.setAttribute('y2', Math.sin(thighAngle) * L1);
// Shin Group moves to end of thigh
els.shinGroup.setAttribute('transform', `translate(${Math.cos(thighAngle)*L1}, ${Math.sin(thighAngle)*L1})`);
// Shin rotation (Thigh angle + Knee Angle)
// Note: Knee bends backwards, so we add or subtract depending on coord system.
// Standard IK usually bends one way. Let's add PI - kneeAngle
const shinGlobalAngle = thighAngle + (Math.PI - kneeAngle);
els.shin.setAttribute('x2', Math.cos(shinGlobalAngle) * L2);
els.shin.setAttribute('y2', Math.sin(shinGlobalAngle) * L2);
// Foot Group moves to end of shin
// We orient the foot to be flat-ish on the pedal
// Pedal is rotating. We want foot to stay horizontal-ish relative to ground,
// or rotate with pedal. Let's rotate with pedal for realism.
els.footGroup.setAttribute('transform', `translate(${Math.cos(shinGlobalAngle)*L2}, ${Math.sin(shinGlobalAngle)*L2}) rotate(${state.pedalAngle * 0.5})`);
// 4. Wing Flapping
// Sine wave based on time
const flapBase = Math.sin(state.time * 0.5);
const flapDeg = flapBase * state.flapIntensity * 5; // Multiplier for range
// Pivot is around body center approx
els.wing.setAttribute('transform', `translate(420, 270) rotate(${flapDeg})`);
// 5. Head Bobbing
const bobY = Math.sin(state.time * 0.2) * 2;
els.head.setAttribute('transform', `translate(460, ${240 + bobY}) rotate(-10)`);
}
function loop() {
updatePhysics();
requestAnimationFrame(loop);
}
// === Event Listeners ===
els.toggleBtn.addEventListener('click', () => {
state.isPlaying = !state.isPlaying;
els.toggleBtn.textContent = state.isPlaying ? "Pause" : "Resume";
});
els.speedRange.addEventListener('input', (e) => {
state.speed = parseFloat(e.target.value);
});
els.flapRange.addEventListener('input', (e) => {
state.flapIntensity = parseFloat(e.target.value);
});
// === Start ===
loop();
</script>
</body>
</html>17 input, 5,459 output, {"completion_tokens_details": {"reasoning_tokens": 673}, "cost": 0.02185215, "cost_details": {"upstream_inference_cost": 0.02185215, "upstream_inference_prompt_cost": 1.615e-05, "upstream_inference_completions_cost": 0.021836}}