User prompt
end of the song or game give a overall for episode level like S is higher then A then B then C ,D is the lowest point u would get and give a button for going game menu
User prompt
at the start of the game there has to a menu for selection song make it
User prompt
i cannot see anything in menu fix it
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'time')' in or related to this line: 'if (!songEnded && currentTime > notes[notes.length - 1].time + 3000) {' Line Number: 892
User prompt
at the start of the game there has to be a menu to choose which song do i wanna play make 5 diffirent song for it and make it long song like 2 munites
User prompt
i have to click and every where for my pitchbaractive
User prompt
diagonal note is move up when it came to las chance to catch dont do just notes have to slide right to left dont go up or down for all
User prompt
diagonal note is move up when it came to las chance to catch
User prompt
some notes moving up when i caught them dont to that
User prompt
dont that notes circle make it horizontal rectangle and do not with flat make curvy
User prompt
the notes has to be like non flat line we have to click and hold for 1 seconds or more or les it depends by difficult
User prompt
make that note glide horizontal not vertical rotate it and dont do flaty note it has to be click and hold for all
User prompt
make that note glide horizontal not vertical
User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'feedbackTxt.style.fill = color;' Line Number: 366
Code edit (1 edits merged)
Please save this source code
User prompt
Trombone Slide Showdown
Initial prompt
Create a 2D full-screen horizontal rhythm game where the player simulates playing a trombone by controlling both pitch and timing as notes scroll from right to left across a horizontal musical track in the center of the screen. The player interface features a dynamic vertical pitch bar on the left side and a series of note paths extending across the screen, with notes of various types—tap notes, hold notes, and gliding notes—approaching a clearly marked "hit zone" on the left. Players must press and hold a designated button or key (or tap and hold on mobile) while simultaneously sliding their finger or mouse vertically to match the pitch of each note in real-time. For hold or gliding notes, players are required to maintain their hold while precisely adjusting their pitch vertically as the note curves up or down, reflecting the natural sliding behavior of a trombone. Accuracy is determined by how closely the player matches the note’s position as it passes through the hit zone, with visual feedback provided through glow intensity, color shifts, and score pop-ups. The background dynamically responds to the music and performance (e.g., visual effects on streaks or misses), and all gameplay occurs in landscape full-screen mode, ensuring maximum immersion and responsiveness. The system includes real-time audio synthesis or pre-mapped pitch playback triggered by user interaction, with humorous and exaggerated character animations reacting to performance quality. The result is a hilarious, musically chaotic but skill-based rhythm experience where each successful note feels like hitting the perfect trombone slide, and each miss produces a delightful mess of musical mayhem.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Confetti for success var Confetti = Container.expand(function () { var self = Container.call(this); var asset = self.attachAsset('confetti', { anchorX: 0.5, anchorY: 0.5 }); self.vx = (Math.random() - 0.5) * 20; self.vy = -Math.random() * 20 - 10; self.life = 40 + Math.random() * 20; self.update = function () { self.x += self.vx; self.y += self.vy; self.vy += 1.2; self.life--; asset.rotation += 0.2; if (self.life <= 0) { self.destroy(); } }; return self; }); // Fail splash for miss var FailSplash = Container.expand(function () { var self = Container.call(this); var asset = self.attachAsset('failSplash', { anchorX: 0.5, anchorY: 0.5 }); asset.alpha = 0.7; self.life = 20; self.update = function () { asset.scaleX += 0.1; asset.scaleY += 0.1; asset.alpha -= 0.03; self.life--; if (self.life <= 0) { self.destroy(); } }; return self; }); // Note: All note types (tap, hold, glide) are handled by the Note class with type property. var Note = Container.expand(function () { var self = Container.call(this); // Properties: type ('tap', 'hold', 'glide'), pitch (0-1), time, duration (for hold/glide), glideTo (for glide) self.type = 'tap'; self.pitch = 0.5; // 0 (bottom) to 1 (top) self.time = 0; // When the note should be hit (in ms) self.duration = 0; // For hold/glide notes (ms) self.glideTo = null; // For glide notes: target pitch (0-1) // Visuals: Use a horizontal rectangle for the note head var noteAsset = self.attachAsset('holdBar', { anchorX: 0.5, anchorY: 0.5 }); noteAsset.width = 120; noteAsset.height = 48; noteAsset.rotation = 0; if (self.type === 'tap') { noteAsset.tint = 0x66ccff; } else if (self.type === 'hold') { noteAsset.tint = 0x44dd88; } else if (self.type === 'glide') { noteAsset.tint = 0xff66aa; } else { noteAsset.tint = 0xffffff; } self.noteAsset = noteAsset; // Curvy bar for duration: approximate with a sequence of small rectangles to simulate a curve self.curveBars = []; var curveBarCount = 18; // More = smoother for (var i = 0; i < curveBarCount; i++) { var seg = self.attachAsset('holdBar', { anchorX: 0.5, anchorY: 0.5 }); seg.width = 18; seg.height = 24; seg.alpha = 0.7; if (self.type === 'tap') { seg.tint = 0x66ccff; } else if (self.type === 'hold') { seg.tint = 0x44dd88; } else if (self.type === 'glide') { seg.tint = 0xff66aa; } else { seg.tint = 0xffffff; } self.curveBars.push(seg); } // State self.hit = false; // Whether the note has been hit self.missed = false; // Whether the note was missed // For glide: cache start/end pitch self.glideStart = null; self.glideEnd = null; // Update visuals per frame self.update = function () { // All notes are now click-and-hold for a variable duration (not just tap) // The bar is always horizontal and its length reflects the duration // For tap: treat as a short hold, so bar length is based on tapHoldDuration var tapHoldDuration = 400; // ms, how long you must hold for a tap note (difficulty can adjust this) var barLen = 0; var startPitch = self.pitch; var endPitch = self.pitch; if (self.type === 'tap') { barLen = Math.max(80, tapHoldDuration * noteSpeed); self.duration = tapHoldDuration; } else if (self.type === 'hold') { barLen = Math.max(80, (self.duration || 1) * noteSpeed); } else if (self.type === 'glide') { barLen = Math.max(80, (self.duration || 1) * noteSpeed); startPitch = self.glideStart !== null ? self.glideStart : self.pitch; endPitch = self.glideEnd !== null ? self.glideEnd : self.pitch; } // Place the note head at the start of the bar self.noteAsset.x = 0; self.noteAsset.y = 0; // Draw the curvy bar: for tap/hold, a gentle sine curve; for glide, interpolate pitch for (var i = 0; i < self.curveBars.length; i++) { var seg = self.curveBars[i]; var t = i / (self.curveBars.length - 1); var x = t * barLen; var y = 0; if (self.type === 'tap' || self.type === 'hold') { // Gentle sine wave for fun y = Math.sin(t * Math.PI) * 18; } else if (self.type === 'glide') { // Interpolate pitch for glide var pitch = lerp(startPitch, endPitch, t); var y0 = pitchToY(startPitch); var y1 = pitchToY(endPitch); y = lerp(y0, y1, t) - pitchToY(self.pitch); } seg.x = x - barLen / 2 + 60; // Center bar, offset so note head is at left seg.y = y; seg.rotation = 0; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222244 }); /**** * Game Code ****/ // --- Constants --- // Trombone Slide Showdown - Asset Initialization // Note: Asset creation is handled automatically by LK based on usage below. // Trombone slide bar (vertical pitch bar) // Note types // Hold bar (for hold notes) // Hit zone // Comedic feedback // Sounds (placeholders, actual sound assets will be loaded by LK) // Music (background track) var gameWidth = 2048; var gameHeight = 2732; // Pitch bar (vertical, left side) var pitchBarHeight = 1800; var pitchBarY = (gameHeight - pitchBarHeight) / 2; var pitchBarX = 120; // Hit zone (where notes should be hit) var hitZoneX = 340; var hitZoneWidth = 140; // Note scroll area var noteStartX = gameWidth + 200; // Offscreen right var noteEndX = hitZoneX; // Hit zone // Note speed (pixels per ms) var noteSpeed = 1.2; // px/ms (tune for difficulty) // --- State --- var notes = []; // All notes in the song var activeNotes = []; // Notes currently on screen var currentTime = 0; // ms since song start var songStarted = false; var songEnded = false; var score = 0; var combo = 0; var maxCombo = 0; var accuracySum = 0; var accuracyCount = 0; // --- UI Elements --- // Pitch bar background var pitchBarBg = LK.getAsset('pitchBarBg', { anchorX: 0.5, anchorY: 0 }); pitchBarBg.x = pitchBarX; pitchBarBg.y = pitchBarY; game.addChild(pitchBarBg); // Pitch bar active marker (shows current player pitch) var pitchBarActive = LK.getAsset('pitchBarActive', { anchorX: 0.5, anchorY: 0.5 }); pitchBarActive.x = pitchBarX; pitchBarActive.y = pitchBarY + pitchBarHeight / 2; game.addChild(pitchBarActive); // Hit zone var hitZone = LK.getAsset('hitZone', { anchorX: 0.5, anchorY: 0.5 }); hitZone.x = hitZoneX; hitZone.y = gameHeight / 2; hitZone.alpha = 0.13; game.addChild(hitZone); // Score text var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Combo text var comboTxt = new Text2('', { size: 80, fill: 0xFFE066 }); comboTxt.anchor.set(0.5, 0); LK.gui.top.addChild(comboTxt); comboTxt.y = 130; // Feedback text (shows "Great!", "Miss!", etc) var feedbackTxt = new Text2('', { size: 120, fill: 0xFF66AA }); feedbackTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(feedbackTxt); // --- Player Pitch State --- var playerPitch = 0.5; // 0 (bottom) to 1 (top) var isSliding = false; // --- Song Data (MVP: simple hardcoded song) --- // Each note: {type, time, pitch, duration, glideTo} notes = [ // Tap notes { type: 'tap', time: 1000, pitch: 0.2 }, { type: 'tap', time: 1800, pitch: 0.7 }, { type: 'tap', time: 2600, pitch: 0.5 }, // Hold note { type: 'hold', time: 3400, pitch: 0.3, duration: 1200 }, // Glide note { type: 'glide', time: 5000, pitch: 0.8, duration: 1200, glideTo: 0.2 }, // More tap notes { type: 'tap', time: 6700, pitch: 0.6 }, { type: 'tap', time: 7400, pitch: 0.4 }, { type: 'tap', time: 8100, pitch: 0.5 }, // Hold { type: 'hold', time: 8800, pitch: 0.7, duration: 1000 }, // Glide { type: 'glide', time: 10200, pitch: 0.2, duration: 1000, glideTo: 0.8 } // End ]; // --- Helper Functions --- function clamp(val, min, max) { return val < min ? min : val > max ? max : val; } function lerp(a, b, t) { return a + (b - a) * t; } // Converts pitch (0-1) to y position on pitch bar function pitchToY(pitch) { return pitchBarY + (1 - pitch) * pitchBarHeight; } // Converts y position to pitch (0-1) function yToPitch(y) { var rel = (y - pitchBarY) / pitchBarHeight; return clamp(1 - rel, 0, 1); } // Spawns a note object and adds to game function spawnNote(noteData) { var note = new Note(); note.type = noteData.type; note.pitch = noteData.pitch; note.time = noteData.time; note.duration = noteData.duration || 0; note.glideTo = noteData.glideTo !== undefined ? noteData.glideTo : null; note.hit = false; note.missed = false; note.x = noteStartX; note.y = pitchToY(note.pitch); // For glide: cache start/end pitch if (note.type === 'glide') { note.glideStart = note.pitch; note.glideEnd = note.glideTo; } // All notes are now click-and-hold (handled in game.update logic) // Visuals are handled in Note class (rotated, horizontal bar) activeNotes.push(note); game.addChild(note); } // Shows feedback text and clears after a short time function showFeedback(text, color) { feedbackTxt.setText(text); feedbackTxt.setStyle({ fill: color }); feedbackTxt.alpha = 1; tween(feedbackTxt, { alpha: 0 }, { duration: 700, easing: tween.linear }); } // Spawns confetti at (x, y) function spawnConfetti(x, y) { for (var i = 0; i < 10; i++) { var c = new Confetti(); c.x = x; c.y = y; game.addChild(c); } } // Spawns fail splash at (x, y) function spawnFailSplash(x, y) { var s = new FailSplash(); s.x = x; s.y = y; game.addChild(s); } // --- Input Handling --- // Dragging on pitch bar game.down = function (x, y, obj) { // Only allow drag if on left 300px (pitch bar area) if (x < 300) { isSliding = true; playerPitch = yToPitch(y); updatePitchBar(); } }; game.move = function (x, y, obj) { if (isSliding) { playerPitch = yToPitch(y); updatePitchBar(); } }; game.up = function (x, y, obj) { isSliding = false; }; // Update pitch bar marker position function updatePitchBar() { pitchBarActive.y = pitchToY(playerPitch); } // --- Game Loop --- game.update = function () { if (!songStarted) { // Start music and timer LK.playMusic('bgmusic'); songStarted = true; currentTime = 0; score = 0; combo = 0; maxCombo = 0; accuracySum = 0; accuracyCount = 0; scoreTxt.setText('0'); comboTxt.setText(''); feedbackTxt.setText(''); // Remove any old notes for (var i = activeNotes.length - 1; i >= 0; i--) { activeNotes[i].destroy(); activeNotes.splice(i, 1); } } // Advance time currentTime += 1000 / 60; // 60 FPS // Spawn notes as they come into view for (var i = 0; i < notes.length; i++) { var noteData = notes[i]; if (!noteData.spawned && noteData.time - (noteStartX - hitZoneX) / noteSpeed < currentTime) { spawnNote(noteData); noteData.spawned = true; } } // Update notes for (var i = activeNotes.length - 1; i >= 0; i--) { var note = activeNotes[i]; // Calculate note's current x based on time var noteTime = note.time; var t = noteTime - currentTime; note.x = hitZoneX + t * noteSpeed; // For all notes: y position is fixed to their initial pitch (no vertical movement as they approach the hit zone) if (!note.hit && !note.missed) { note.y = pitchToY(note.pitch); } // If hit or missed, do not update y (freeze at last value) // Remove notes that have gone offscreen left if (note.x < -200) { note.destroy(); activeNotes.splice(i, 1); continue; } // Check for hit/miss if (!note.hit && !note.missed) { // Tap note: now must be click-and-hold in hit zone for a short time (like a mini-hold) if (note.type === 'tap') { var tapHoldDuration = 400; // ms, must match Note class var hitWindow = tapHoldDuration; // ms, tap is now a short hold var timeDiff = Math.abs(currentTime - note.time); var inHitZone = Math.abs(note.x - hitZoneX) < hitZoneWidth / 2; var pitchDiff = Math.abs(playerPitch - note.pitch); if (inHitZone && timeDiff < hitWindow) { if (isSliding && pitchDiff < 0.12) { // Good hold (tap is now a short hold) if (!note.holdStarted) { note.holdStarted = true; note.holdScore = 0; note.holdTicks = 0; LK.getSound('slide').play(); } note.holdScore += 1; note.holdTicks += 1; note.noteAsset.scaleX = 1.2; note.noteAsset.scaleY = 1.2; } else if (note.holdStarted) { note.noteAsset.scaleX = 1; note.noteAsset.scaleY = 1; } } // End of tap "hold" (after hitWindow) if (currentTime > note.time + hitWindow && !note.hit) { note.hit = true; var tapAcc = note.holdTicks ? note.holdScore / note.holdTicks : 0; if (tapAcc > 0.7) { score += 100; combo += 1; if (combo > maxCombo) maxCombo = combo; accuracySum += tapAcc; accuracyCount++; scoreTxt.setText(score + ''); comboTxt.setText(combo > 1 ? combo + ' Combo!' : ''); showFeedback('Great!', "#ffe066"); spawnConfetti(note.x, note.y); LK.getSound('hit').play(); } else { combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } // Missed if passed hit window and not started if (currentTime > note.time + hitWindow + 200 && !note.hit) { note.missed = true; combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } // Hold note: must hold correct pitch during duration (horizontal, click-and-hold) else if (note.type === 'hold') { var holdStart = note.time; var holdEnd = note.time + note.duration; var inHoldZone = currentTime >= holdStart - 180 && currentTime <= holdEnd + 180; var inHitZone = Math.abs(note.x - hitZoneX) < hitZoneWidth / 2; if (inHoldZone && inHitZone) { if (isSliding && Math.abs(playerPitch - note.pitch) < 0.13) { // Good hold if (!note.holdStarted) { note.holdStarted = true; note.holdScore = 0; note.holdTicks = 0; LK.getSound('slide').play(); } note.holdScore += 1; note.holdTicks += 1; // Visual feedback note.noteAsset.scaleX = 1.2; note.noteAsset.scaleY = 1.2; } else if (note.holdStarted) { note.noteAsset.scaleX = 1; note.noteAsset.scaleY = 1; } } // End of hold if (currentTime > holdEnd && !note.hit) { note.hit = true; var holdAcc = note.holdTicks ? note.holdScore / note.holdTicks : 0; if (holdAcc > 0.7) { score += 180; combo += 1; if (combo > maxCombo) maxCombo = combo; accuracySum += holdAcc; accuracyCount++; scoreTxt.setText(score + ''); comboTxt.setText(combo > 1 ? combo + ' Combo!' : ''); showFeedback('Hold!', "#44dd88"); spawnConfetti(note.x, note.y); LK.getSound('tromboneGood').play(); } else { combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } // Missed if passed hold window and not started if (currentTime > holdEnd + 200 && !note.hit) { note.missed = true; combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } // Glide note: must follow pitch from start to end (horizontal, click-and-hold) else if (note.type === 'glide') { var glideStart = note.time; var glideEnd = note.time + note.duration; var inGlideZone = currentTime >= glideStart - 180 && currentTime <= glideEnd + 180; var inHitZone = Math.abs(note.x - hitZoneX) < hitZoneWidth / 2; if (inGlideZone && inHitZone) { var glideT = clamp((currentTime - glideStart) / note.duration, 0, 1); var targetPitch = lerp(note.glideStart, note.glideEnd, glideT); var pitchDiff = Math.abs(playerPitch - targetPitch); if (isSliding && pitchDiff < 0.15) { // Good glide if (!note.glideStarted) { note.glideStarted = true; note.glideScore = 0; note.glideTicks = 0; LK.getSound('slide').play(); } note.glideScore += 1; note.glideTicks += 1; note.noteAsset.scaleX = 1.2; note.noteAsset.scaleY = 1.2; } else if (note.glideStarted) { note.noteAsset.scaleX = 1; note.noteAsset.scaleY = 1; } } // End of glide if (currentTime > glideEnd && !note.hit) { note.hit = true; var glideAcc = note.glideTicks ? note.glideScore / note.glideTicks : 0; if (glideAcc > 0.7) { score += 200; combo += 1; if (combo > maxCombo) maxCombo = combo; accuracySum += glideAcc; accuracyCount++; scoreTxt.setText(score + ''); comboTxt.setText(combo > 1 ? combo + ' Combo!' : ''); showFeedback('Glide!', "#ff66aa"); spawnConfetti(note.x, note.y); LK.getSound('tromboneGood').play(); } else { combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } // Missed if passed glide window and not started if (currentTime > glideEnd + 200 && !note.hit) { note.missed = true; combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } } } // Remove hit/missed notes after a delay for (var i = activeNotes.length - 1; i >= 0; i--) { var note = activeNotes[i]; if ((note.hit || note.missed) && currentTime - note.time > 800) { note.destroy(); activeNotes.splice(i, 1); } } // End of song if (!songEnded && currentTime > notes[notes.length - 1].time + 3000) { songEnded = true; // Show results var acc = accuracyCount ? Math.round(accuracySum / accuracyCount * 100) : 0; var result = "Score: " + score + "\nMax Combo: " + maxCombo + "\nAccuracy: " + acc + "%"; showFeedback("Showdown Complete!\n" + result, "#ffe066"); // Show win if score is high enough if (score > 800) { LK.showYouWin(); } else { LK.showGameOver(); } } }; // --- Start music --- LK.playMusic('bgmusic', { fade: { start: 0, end: 1, duration: 1000 } });
===================================================================
--- original.js
+++ change.js
@@ -158,18 +158,18 @@
/****
* Game Code
****/
-// Music (background track)
-// Sounds (placeholders, actual sound assets will be loaded by LK)
-// Comedic feedback
-// Hit zone
-// Hold bar (for hold notes)
-// Note types
-// Trombone slide bar (vertical pitch bar)
-// Note: Asset creation is handled automatically by LK based on usage below.
-// Trombone Slide Showdown - Asset Initialization
// --- Constants ---
+// Trombone Slide Showdown - Asset Initialization
+// Note: Asset creation is handled automatically by LK based on usage below.
+// Trombone slide bar (vertical pitch bar)
+// Note types
+// Hold bar (for hold notes)
+// Hit zone
+// Comedic feedback
+// Sounds (placeholders, actual sound assets will be loaded by LK)
+// Music (background track)
var gameWidth = 2048;
var gameHeight = 2732;
// Pitch bar (vertical, left side)
var pitchBarHeight = 1800;
@@ -436,16 +436,11 @@
// Calculate note's current x based on time
var noteTime = note.time;
var t = noteTime - currentTime;
note.x = hitZoneX + t * noteSpeed;
- // For glide: update y position over duration, but freeze y if hit or missed
+ // For all notes: y position is fixed to their initial pitch (no vertical movement as they approach the hit zone)
if (!note.hit && !note.missed) {
- if (note.type === 'glide' && currentTime >= note.time && currentTime <= note.time + note.duration) {
- var glideT = (currentTime - note.time) / note.duration;
- note.y = pitchToY(lerp(note.glideStart, note.glideEnd, glideT));
- } else {
- note.y = pitchToY(note.pitch);
- }
+ note.y = pitchToY(note.pitch);
}
// If hit or missed, do not update y (freeze at last value)
// Remove notes that have gone offscreen left
if (note.x < -200) {
@@ -628,25 +623,9 @@
comboTxt.setText('');
showFeedback('Miss!', "#ff3333");
spawnFailSplash(note.x, note.y);
LK.getSound('miss').play();
- // Make diagonal (glide) notes move up when missed (last chance to catch)
- note.glideMissMoveUp = true;
- note.glideMissVy = -18 - Math.random() * 8; // move up fast
- note.glideMissVx = 8 + Math.random() * 8; // drift right
}
- // If missed and flagged, move up
- if (note.type === 'glide' && note.missed && note.glideMissMoveUp) {
- note.x += note.glideMissVx || 0;
- note.y += note.glideMissVy || 0;
- note.glideMissVy += 1.2; // gravity
- // Fade out and destroy after offscreen
- if (note.y < -100 || note.x > gameWidth + 200) {
- note.destroy();
- activeNotes.splice(i, 1);
- continue;
- }
- }
}
}
}
// Remove hit/missed notes after a delay
make a circle red. In-Game asset. 2d. High contrast. No shadows
give me black circle. In-Game asset. 2d. High contrast. No shadows
make a episode complete background for trambone game but without any text.. In-Game asset. 2d. High contrast. No shadows
make a backgroud shaped pear but dont do pear. In-Game asset. 2d. High contrast. No shadows
make a fully white eye like a just oval but without pupil. In-Game asset. 2d. High contrast. No shadows
nose. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
make a just face witout eyes and eyebrows and hairs and nose and mouth. In-Game asset. 2d. High contrast. No shadows
make a straight closed mouth. In-Game asset. 2d. High contrast. No shadows
make a laughed mouth. In-Game asset. 2d. High contrast. No shadows
make a red curtain but just curtain like real 3d. In-Game asset. High contrast. shadow
make a selection menu background but without any text.. In-Game asset. 2d. High contrast. No shadows
make a background like trambone champ game. In-Game asset. 2d. High contrast. No shadows
make a 2d trambone image. In-Game asset. 2d. High contrast