/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var GridCell = Container.expand(function (trackIndex, stepIndex, soundId) { var self = Container.call(this); self.trackIndex = trackIndex; self.stepIndex = stepIndex; self.soundId = soundId; self.isActive = false; var cellBg = self.attachAsset('gridCell', { anchorX: 0.5, anchorY: 0.5 }); var activeBeat = self.attachAsset('activeBeat', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); self.setActive = function (active) { self.isActive = active; if (active) { activeBeat.alpha = 1; tween(activeBeat, { scaleX: 1.1, scaleY: 1.1 }, { duration: 100, easing: tween.easeOut }); tween(activeBeat, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeIn }); } else { activeBeat.alpha = 0; } }; self.highlight = function () { if (self.isActive) { tween(activeBeat, { scaleX: 1.2, scaleY: 1.2 }, { duration: 50, easing: tween.easeOut }); tween(activeBeat, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeIn }); } }; self.down = function (x, y, obj) { self.setActive(!self.isActive); if (self.isActive) { LK.getSound(self.soundId).play(); } }; return self; }); var Track = Container.expand(function (trackIndex, soundId, color, name) { var self = Container.call(this); self.trackIndex = trackIndex; self.soundId = soundId; self.cells = []; var trackBg = self.attachAsset('trackBackground', { anchorX: 0, anchorY: 0.5, tint: color }); trackBg.alpha = 0.3; trackBg.y = 0; var trackLabel = new Text2(name, { size: 32, fill: 0xFFFFFF }); trackLabel.anchor.set(0, 0.5); trackLabel.x = -30; trackLabel.y = 0; self.addChild(trackLabel); for (var i = 0; i < 12; i++) { var cell = new GridCell(trackIndex, i, soundId); cell.x = 150 + i * 160; cell.y = 0; self.addChild(cell); self.cells.push(cell); } self.playStep = function (stepIndex) { if (self.cells[stepIndex].isActive) { LK.getSound(self.soundId).play(); self.cells[stepIndex].highlight(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0d1117 }); /**** * Game Code ****/ // Initialize sound assets for each track var isPlaying = false; var currentStep = 0; var bpm = 115; var stepDuration = 60 / bpm / 4 * 1000; var lastStepTime = 0; var tracks = []; var trackData = [{ soundId: 'kick', color: 0xFF5722, name: 'KICK' }, { soundId: 'snare', color: 0x2196F3, name: 'SNARE' }, { soundId: 'hihat', color: 0xFFC107, name: 'HI-HAT' }, { soundId: 'bass', color: 0x9C27B0, name: 'BASS' }, { soundId: 'synth', color: 0x00BCD4, name: 'RIM' }, { soundId: 'lead', color: 0x8BC34A, name: 'CLAP' }]; // Create tracks - center vertically on screen var totalTracksHeight = trackData.length * 100; var startY = (2732 - totalTracksHeight) / 2; for (var i = 0; i < trackData.length; i++) { var track = new Track(i, trackData[i].soundId, trackData[i].color, trackData[i].name); track.x = 32; track.y = startY + i * 100; game.addChild(track); tracks.push(track); } // Create playhead var playhead = game.attachAsset('playhead', { anchorX: 0.5, anchorY: 0 }); playhead.x = 150 + 32; playhead.y = startY - 50; // Create play/pause button var playButton = game.attachAsset('playButton', { anchorX: 0.5, anchorY: 0.5 }); playButton.x = 1024; playButton.y = 350; var pauseButton = game.attachAsset('pauseButton', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); pauseButton.x = 1024; pauseButton.y = 350; // BPM display var bpmText = new Text2('BPM: ' + bpm, { size: 48, fill: 0xFFFFFF }); bpmText.anchor.set(0.5, 0.5); bpmText.x = 1024; bpmText.y = 500; game.addChild(bpmText); // Step counter var stepText = new Text2('Step: 1/12', { size: 48, fill: 0xFFFFFF }); stepText.anchor.set(0.5, 0.5); stepText.x = 1024; stepText.y = 600; game.addChild(stepText); // BPM controls var bpmUpButton = new Text2('+', { size: 100, fill: 0x4CAF50 }); bpmUpButton.anchor.set(0.5, 0.5); bpmUpButton.x = 1100; bpmUpButton.y = 700; game.addChild(bpmUpButton); var bpmDownButton = new Text2('-', { size: 100, fill: 0xFF5722 }); bpmDownButton.anchor.set(0.5, 0.5); bpmDownButton.x = 950; bpmDownButton.y = 700; game.addChild(bpmDownButton); function togglePlayback() { isPlaying = !isPlaying; if (isPlaying) { playButton.alpha = 0; pauseButton.alpha = 1; lastStepTime = Date.now(); } else { playButton.alpha = 1; pauseButton.alpha = 0; } } function changeBPM(delta) { bpm = Math.max(60, Math.min(200, bpm + delta)); stepDuration = 60 / bpm / 4 * 1000; bpmText.setText('BPM: ' + bpm); } playButton.down = function (x, y, obj) { togglePlayback(); }; pauseButton.down = function (x, y, obj) { togglePlayback(); }; bpmUpButton.down = function (x, y, obj) { changeBPM(5); tween(bpmUpButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100 }); tween(bpmUpButton, { scaleX: 1, scaleY: 1 }, { duration: 100 }); }; bpmDownButton.down = function (x, y, obj) { changeBPM(-5); tween(bpmDownButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100 }); tween(bpmDownButton, { scaleX: 1, scaleY: 1 }, { duration: 100 }); }; // Load saved pattern - handle flattened storage structure var savedPattern = storage.pattern || {}; for (var trackIndex = 0; trackIndex < tracks.length; trackIndex++) { for (var stepIndex = 0; stepIndex < 12; stepIndex++) { var key = trackIndex + '_' + stepIndex; if (savedPattern[key]) { tracks[trackIndex].cells[stepIndex].setActive(true); } } } game.update = function () { if (isPlaying) { var currentTime = Date.now(); if (currentTime - lastStepTime >= stepDuration) { // Play current step for (var i = 0; i < tracks.length; i++) { tracks[i].playStep(currentStep); } // Move to next step currentStep = (currentStep + 1) % 12; stepText.setText('Step: ' + (currentStep + 1) + '/12'); // Update playhead position playhead.x = 150 + 32 + currentStep * 160; lastStepTime = currentTime; // Auto-save pattern - flatten to single depth for storage compatibility var pattern = {}; for (var trackIndex = 0; trackIndex < tracks.length; trackIndex++) { for (var stepIndex = 0; stepIndex < 12; stepIndex++) { var key = trackIndex + '_' + stepIndex; pattern[key] = tracks[trackIndex].cells[stepIndex].isActive; } } storage.pattern = pattern; } } }; // Title var titleText = new Text2('BM Studio', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 100; game.addChild(titleText); // Instructions var instructText = new Text2('Tap grid to add beats • Press play to start', { size: 20, fill: 0x888888 }); instructText.anchor.set(0.5, 0); instructText.x = 1024; instructText.y = 2600; game.addChild(instructText);
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var GridCell = Container.expand(function (trackIndex, stepIndex, soundId) {
var self = Container.call(this);
self.trackIndex = trackIndex;
self.stepIndex = stepIndex;
self.soundId = soundId;
self.isActive = false;
var cellBg = self.attachAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
var activeBeat = self.attachAsset('activeBeat', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.setActive = function (active) {
self.isActive = active;
if (active) {
activeBeat.alpha = 1;
tween(activeBeat, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 100,
easing: tween.easeOut
});
tween(activeBeat, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeIn
});
} else {
activeBeat.alpha = 0;
}
};
self.highlight = function () {
if (self.isActive) {
tween(activeBeat, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 50,
easing: tween.easeOut
});
tween(activeBeat, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeIn
});
}
};
self.down = function (x, y, obj) {
self.setActive(!self.isActive);
if (self.isActive) {
LK.getSound(self.soundId).play();
}
};
return self;
});
var Track = Container.expand(function (trackIndex, soundId, color, name) {
var self = Container.call(this);
self.trackIndex = trackIndex;
self.soundId = soundId;
self.cells = [];
var trackBg = self.attachAsset('trackBackground', {
anchorX: 0,
anchorY: 0.5,
tint: color
});
trackBg.alpha = 0.3;
trackBg.y = 0;
var trackLabel = new Text2(name, {
size: 32,
fill: 0xFFFFFF
});
trackLabel.anchor.set(0, 0.5);
trackLabel.x = -30;
trackLabel.y = 0;
self.addChild(trackLabel);
for (var i = 0; i < 12; i++) {
var cell = new GridCell(trackIndex, i, soundId);
cell.x = 150 + i * 160;
cell.y = 0;
self.addChild(cell);
self.cells.push(cell);
}
self.playStep = function (stepIndex) {
if (self.cells[stepIndex].isActive) {
LK.getSound(self.soundId).play();
self.cells[stepIndex].highlight();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0d1117
});
/****
* Game Code
****/
// Initialize sound assets for each track
var isPlaying = false;
var currentStep = 0;
var bpm = 115;
var stepDuration = 60 / bpm / 4 * 1000;
var lastStepTime = 0;
var tracks = [];
var trackData = [{
soundId: 'kick',
color: 0xFF5722,
name: 'KICK'
}, {
soundId: 'snare',
color: 0x2196F3,
name: 'SNARE'
}, {
soundId: 'hihat',
color: 0xFFC107,
name: 'HI-HAT'
}, {
soundId: 'bass',
color: 0x9C27B0,
name: 'BASS'
}, {
soundId: 'synth',
color: 0x00BCD4,
name: 'RIM'
}, {
soundId: 'lead',
color: 0x8BC34A,
name: 'CLAP'
}];
// Create tracks - center vertically on screen
var totalTracksHeight = trackData.length * 100;
var startY = (2732 - totalTracksHeight) / 2;
for (var i = 0; i < trackData.length; i++) {
var track = new Track(i, trackData[i].soundId, trackData[i].color, trackData[i].name);
track.x = 32;
track.y = startY + i * 100;
game.addChild(track);
tracks.push(track);
}
// Create playhead
var playhead = game.attachAsset('playhead', {
anchorX: 0.5,
anchorY: 0
});
playhead.x = 150 + 32;
playhead.y = startY - 50;
// Create play/pause button
var playButton = game.attachAsset('playButton', {
anchorX: 0.5,
anchorY: 0.5
});
playButton.x = 1024;
playButton.y = 350;
var pauseButton = game.attachAsset('pauseButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
pauseButton.x = 1024;
pauseButton.y = 350;
// BPM display
var bpmText = new Text2('BPM: ' + bpm, {
size: 48,
fill: 0xFFFFFF
});
bpmText.anchor.set(0.5, 0.5);
bpmText.x = 1024;
bpmText.y = 500;
game.addChild(bpmText);
// Step counter
var stepText = new Text2('Step: 1/12', {
size: 48,
fill: 0xFFFFFF
});
stepText.anchor.set(0.5, 0.5);
stepText.x = 1024;
stepText.y = 600;
game.addChild(stepText);
// BPM controls
var bpmUpButton = new Text2('+', {
size: 100,
fill: 0x4CAF50
});
bpmUpButton.anchor.set(0.5, 0.5);
bpmUpButton.x = 1100;
bpmUpButton.y = 700;
game.addChild(bpmUpButton);
var bpmDownButton = new Text2('-', {
size: 100,
fill: 0xFF5722
});
bpmDownButton.anchor.set(0.5, 0.5);
bpmDownButton.x = 950;
bpmDownButton.y = 700;
game.addChild(bpmDownButton);
function togglePlayback() {
isPlaying = !isPlaying;
if (isPlaying) {
playButton.alpha = 0;
pauseButton.alpha = 1;
lastStepTime = Date.now();
} else {
playButton.alpha = 1;
pauseButton.alpha = 0;
}
}
function changeBPM(delta) {
bpm = Math.max(60, Math.min(200, bpm + delta));
stepDuration = 60 / bpm / 4 * 1000;
bpmText.setText('BPM: ' + bpm);
}
playButton.down = function (x, y, obj) {
togglePlayback();
};
pauseButton.down = function (x, y, obj) {
togglePlayback();
};
bpmUpButton.down = function (x, y, obj) {
changeBPM(5);
tween(bpmUpButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100
});
tween(bpmUpButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
};
bpmDownButton.down = function (x, y, obj) {
changeBPM(-5);
tween(bpmDownButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100
});
tween(bpmDownButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
};
// Load saved pattern - handle flattened storage structure
var savedPattern = storage.pattern || {};
for (var trackIndex = 0; trackIndex < tracks.length; trackIndex++) {
for (var stepIndex = 0; stepIndex < 12; stepIndex++) {
var key = trackIndex + '_' + stepIndex;
if (savedPattern[key]) {
tracks[trackIndex].cells[stepIndex].setActive(true);
}
}
}
game.update = function () {
if (isPlaying) {
var currentTime = Date.now();
if (currentTime - lastStepTime >= stepDuration) {
// Play current step
for (var i = 0; i < tracks.length; i++) {
tracks[i].playStep(currentStep);
}
// Move to next step
currentStep = (currentStep + 1) % 12;
stepText.setText('Step: ' + (currentStep + 1) + '/12');
// Update playhead position
playhead.x = 150 + 32 + currentStep * 160;
lastStepTime = currentTime;
// Auto-save pattern - flatten to single depth for storage compatibility
var pattern = {};
for (var trackIndex = 0; trackIndex < tracks.length; trackIndex++) {
for (var stepIndex = 0; stepIndex < 12; stepIndex++) {
var key = trackIndex + '_' + stepIndex;
pattern[key] = tracks[trackIndex].cells[stepIndex].isActive;
}
}
storage.pattern = pattern;
}
}
};
// Title
var titleText = new Text2('BM Studio', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 100;
game.addChild(titleText);
// Instructions
var instructText = new Text2('Tap grid to add beats • Press play to start', {
size: 20,
fill: 0x888888
});
instructText.anchor.set(0.5, 0);
instructText.x = 1024;
instructText.y = 2600;
game.addChild(instructText);