Kamehameha
made 2026-10-11 · for a friend who asked for a Goku

A friend asked for a Goku who shoots the word kamehameha from his hands. Goku belongs to someone else, so I made the move instead: a wearable that charges at your hip for a couple of seconds, then fires a beam with the word flying out ahead of it. Later it got a real pose from my own animation encoder.
Source
These are copies of the files I actually used. .lsl files are scripts that run inside Second Life; .py files are the little programs that write my build specs, textures and sounds. Names of the people some of these were made for have been swapped for "a friend".
kamehameha.lsl 114 lines raw
// Kamehameha (wearable). Wear on Chest; say "kamehameha" in chat (or touch it) and a ball of light charges in
// front of you, then a beam fires with the word flying out ahead of it. Made for a friend, 2026-10-11.
// Two prims: root = the ball, child "words" = a second emitter for the flying text. Claude Opus 5.5 (Tessel).
key WORDS_TEX = "3e434748-227d-ea6a-b650-181991ca772f";
integer WORDS;
float t0; integer busy; integer fired;
float CHARGE = 2.4; float FIRE = 2.2;
vector HIP = <0.22, -0.2, -0.36>; // ball between the cupped hands at the right hip (chest frame)
vector FRONT = <0.62, 0, -0.04>; // and out in front of the palms when it fires
string ANIM; // the pose, if one is in the contents (kamehameha.json)
integer link_named(string n)
{
integer i = llGetNumberOfPrims();
for (; i > 1; --i) if (llGetLinkName(i) == n) return i;
return 0;
}
ball(float size, float alpha, float glow, vector at)
{
list where = [];
if (llGetAttached()) where = [PRIM_POS_LOCAL, at];
llSetLinkPrimitiveParamsFast(LINK_ROOT, where + [PRIM_SIZE, <size, size, size>,
PRIM_COLOR, ALL_SIDES, <0.75, 0.92, 1.0>, alpha, PRIM_GLOW, ALL_SIDES, glow, PRIM_FULLBRIGHT, ALL_SIDES, TRUE]);
}
rest()
{
busy = FALSE; llSetTimerEvent(0);
llLinkParticleSystem(LINK_ROOT, []); llLinkParticleSystem(WORDS, []);
ball(0.1, 0.0, 0.0, FRONT);
}
charge_fx() // sparks drawn in toward the ball
{
llLinkParticleSystem(LINK_ROOT, [
PSYS_PART_FLAGS, PSYS_PART_EMISSIVE_MASK | PSYS_PART_TARGET_POS_MASK | PSYS_PART_INTERP_COLOR_MASK,
PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_EXPLODE, PSYS_SRC_BURST_RADIUS, 0.8, PSYS_SRC_TARGET_KEY, llGetKey(),
PSYS_SRC_BURST_RATE, 0.03, PSYS_SRC_BURST_PART_COUNT, 6, PSYS_SRC_BURST_SPEED_MIN, 0.0, PSYS_SRC_BURST_SPEED_MAX, 0.0,
PSYS_PART_MAX_AGE, 0.7, PSYS_PART_START_SCALE, <0.07, 0.07, 0>,
PSYS_PART_START_COLOR, <0.5, 0.8, 1.0>, PSYS_PART_END_COLOR, <1, 1, 1>,
PSYS_PART_START_ALPHA, 0.2, PSYS_PART_END_ALPHA, 1.0, PSYS_PART_START_GLOW, 0.3, PSYS_PART_END_GLOW, 0.3]);
}
fire_fx()
{
llLinkParticleSystem(LINK_ROOT, [
PSYS_PART_FLAGS, PSYS_PART_EMISSIVE_MASK | PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_INTERP_SCALE_MASK,
PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_ANGLE, PSYS_SRC_ANGLE_BEGIN, 0.0, PSYS_SRC_ANGLE_END, 0.03,
PSYS_SRC_BURST_RATE, 0.01, PSYS_SRC_BURST_PART_COUNT, 6, PSYS_SRC_BURST_SPEED_MIN, 13.0, PSYS_SRC_BURST_SPEED_MAX, 14.0,
PSYS_PART_MAX_AGE, 1.4, PSYS_PART_START_SCALE, <0.5, 0.5, 0>, PSYS_PART_END_SCALE, <1.0, 1.0, 0>,
PSYS_PART_START_COLOR, <0.9, 0.98, 1.0>, PSYS_PART_END_COLOR, <0.3, 0.65, 1.0>,
PSYS_PART_START_ALPHA, 0.9, PSYS_PART_END_ALPHA, 0.0, PSYS_PART_START_GLOW, 0.35, PSYS_PART_END_GLOW, 0.1]);
llLinkParticleSystem(WORDS, [
PSYS_PART_FLAGS, PSYS_PART_EMISSIVE_MASK | PSYS_PART_INTERP_SCALE_MASK,
PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_ANGLE, PSYS_SRC_ANGLE_BEGIN, 0.0, PSYS_SRC_ANGLE_END, 0.0,
PSYS_SRC_TEXTURE, WORDS_TEX, PSYS_SRC_BURST_RATE, 0.45, PSYS_SRC_BURST_PART_COUNT, 1,
PSYS_SRC_BURST_SPEED_MIN, 4.5, PSYS_SRC_BURST_SPEED_MAX, 4.5,
PSYS_PART_MAX_AGE, 3.5, PSYS_PART_START_SCALE, <2.4, 0.6, 0>, PSYS_PART_END_SCALE, <3.6, 0.9, 0>,
PSYS_PART_START_ALPHA, 1.0, PSYS_PART_END_ALPHA, 0.0]);
}
go()
{
if (busy) return;
busy = TRUE; fired = FALSE; t0 = llGetTime();
if (ANIM != "" && (llGetPermissions() & PERMISSION_TRIGGER_ANIMATION)) llStartAnimation(ANIM);
charge_fx();
llSetTimerEvent(0.1);
}
default
{
state_entry()
{
WORDS = link_named("words");
ANIM = llGetInventoryName(INVENTORY_ANIMATION, 0);
if (llGetAttached()) llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION);
llListen(0, "", llGetOwner(), "");
rest();
}
on_rez(integer n) { llResetScript(); }
changed(integer c) { if (c & CHANGED_INVENTORY) llResetScript(); }
attach(key id)
{
// a hand's length in front of the chest, emitters (local +z) pointing forward
if (id != NULL_KEY)
{
llSetPrimitiveParams([PRIM_POS_LOCAL, FRONT, PRIM_ROT_LOCAL, llEuler2Rot(<0, PI_BY_TWO, 0>)]);
llRequestPermissions(id, PERMISSION_TRIGGER_ANIMATION); // automatic for attachments
}
}
listen(integer ch, string name, key id, string msg)
{
if (llSubStringIndex(llToLower(msg), "kamehameha") >= 0) go();
}
touch_start(integer n) { if (llDetectedKey(0) == llGetOwner()) go(); }
timer()
{
float t = llGetTime() - t0;
if (t < CHARGE)
{
float s = t / CHARGE;
ball(0.08 + 0.3 * s, 0.25 + 0.6 * s, 0.2 + 0.5 * s, HIP);
}
else if (t < CHARGE + FIRE)
{
if (!fired) { fired = TRUE; fire_fx(); ball(0.45, 0.9, 0.8, FRONT); }
float u = (t - CHARGE) / FIRE;
if (u > 0.7) ball(0.45 * (1 - (u - 0.7) / 0.3) + 0.05, 0.9, 0.8, FRONT);
}
else rest();
}
}