@krishna.dev1/lumajs
v0.3.0
Published
Next-generation JavaScript framework combining React-like reactivity, GSAP-style animations, and Three.js-like 3D rendering in one lightweight library (~40KB total)
Downloads
18
Maintainers
Readme
⚡ LumaJS
Next-generation JavaScript framework combining React-like reactivity, animations, and 3D capabilities in one lightweight library.
🚀 View Demo | 📖 Documentation | 🎯 Hackathon Guide
🌟 Current Status
✅ Step 1: Core Reactivity (Complete)
Complete React-like framework with functional components, hooks, and virtual DOM - all in ~8KB!
✅ Functional Components
✅ Full Hooks System (useState, useEffect, useRef, useMemo, useCallback)
✅ Virtual DOM Rendering
✅ TypeScript Support
✅ Production Ready
✅ Step 2: Animation Engine (Complete)
Professional GSAP-like animation system with advanced features - all in ~14KB!
✅ 30+ Easing Functions (including spring physics)
✅ Timeline Sequencing with Labels
✅ Stagger Animations
✅ Scroll Triggers (IntersectionObserver)
✅ Parallax Effects
✅ Color Morphing
✅ SVG Path Animations
✅ Advanced Controls (pause, resume, reverse, seek)
✅ 20+ Preset Animations
✅ Step 3: 3D Engine (Complete)
Three.js-like WebGL 3D rendering with declarative API - all in ~18KB!
✅ 3D Geometries (Box, Sphere, Plane)
✅ Phong Lighting & Shading
✅ Materials & Colors
✅ Camera System (Perspective)
✅ Orbit Controls
✅ Scene Graph
✅ Matrix Transformations
✅ WebGL Renderer
🎯 What is LumaJS?
LumaJS aims to replace the need for multiple libraries (React + GSAP + Three.js) with one unified framework:
- Reactive & Declarative: Build UIs with automatic state-to-DOM updates
- Animation-Ready: Built-in scroll, hover, click, and time-based triggers (Step 2)
- 3D-Ready: Declarative 3D API on top of WebGL (Step 3)
- Lightweight: ~15–20 KB core (vs 100–200 KB for multiple libraries)
- Plug-and-Play: Works directly in the browser with zero build tools
📦 Installation
Option 1: Direct Browser Usage
<script src="luma-core.js"></script>
<script>
Luma.mount('#app', {
state: { count: 0 }
});
</script>Option 2: NPM (Coming Soon)
npm install lumajs🚀 Quick Start
1. Create an HTML file
<!DOCTYPE html>
<html>
<head>
<title>LumaJS Counter</title>
</head>
<body>
<div id="app">
<h1 l-text="count"></h1>
<button l-on:click="count++">Increment</button>
</div>
<script src="luma-core.js"></script>
<script>
Luma.mount('#app', {
state: { count: 0 }
});
</script>
</body>
</html>2. Open in browser
That's it! You now have a reactive counter with zero build tools.
📖 Core Directives (Step 1)
l-text="expression"
Binds text content to an expression.
<span l-text="username"></span>
<!-- Renders: username from state -->l-html="expression"
Binds innerHTML to an expression.
<div l-html="'<strong>Bold Text</strong>'"></div>l-show="condition"
Shows/hides element based on condition.
<div l-show="isLoggedIn">Welcome back!</div>l-model="path"
Two-way binding for form inputs.
<input type="text" l-model="name">
<p l-text="name"></p>
<!-- Typing updates state and vice versa -->l-on:event="handler"
Event listener binding.
<button l-on:click="count++">Click Me</button>
<input l-on:keydown="if(event.key === 'Enter') submit()">l-bind:attr="expression"
Dynamic attribute binding.
<img l-bind:src="imageUrl" l-bind:alt="imageAlt">
<button l-bind:disabled="!isValid">Submit</button>🏗️ Examples
Counter App
<div id="counter">
<h1 l-text="count"></h1>
<button l-on:click="count++">+</button>
<button l-on:click="count--">-</button>
<button l-on:click="count = 0">Reset</button>
</div>
<script>
Luma.mount('#counter', {
state: { count: 0 }
});
</script>Form Binding
<div id="form">
<input type="text" l-model="name" placeholder="Name">
<input type="email" l-model="email" placeholder="Email">
<p>Hello, <span l-text="name || 'Guest'"></span>!</p>
<p>Your email: <span l-text="email || 'Not provided'"></span></p>
</div>
<script>
Luma.mount('#form', {
state: { name: '', email: '' }
});
</script>Todo List
<div id="todos">
<input l-model="newTodo" l-on:keydown="if(event.key==='Enter') addTodo()">
<button l-on:click="addTodo()">Add</button>
<p l-text="todos.length + ' todos'"></p>
</div>
<script>
Luma.mount('#todos', {
state: {
newTodo: '',
todos: []
},
methods: {
addTodo() {
if (!this.state.newTodo.trim()) return;
this.state.todos.push({
id: Date.now(),
text: this.state.newTodo,
done: false
});
this.state.newTodo = '';
}
}
});
</script>🔧 API Reference
Luma.mount(selector, options)
Mounts a LumaJS app to a DOM element.
Parameters:
selector(string | HTMLElement): Target elementoptions.state(object): Reactive state objectoptions.methods(object): Methods accessible in directives
Returns: { state, methods } context object
const app = Luma.mount('#app', {
state: { count: 0 },
methods: {
increment() { this.state.count++; }
}
});
// Access state programmatically
app.state.count = 10;Luma.reactive(object)
Creates a reactive proxy around an object.
const state = Luma.reactive({ count: 0 });
Luma.effect(() => {
console.log('Count:', state.count);
});
state.count++; // Logs: "Count: 1"Luma.effect(fn)
Runs a function and automatically re-runs when dependencies change.
const state = Luma.reactive({ x: 1, y: 2 });
Luma.effect(() => {
console.log('Sum:', state.x + state.y);
});
state.x = 5; // Logs: "Sum: 7"🎨 Demo
Open demo-step1.html in your browser to see:
- ✅ Counter with increment/decrement
- ✅ Two-way form binding
- ✅ Todo list with add/delete
- ✅ Conditional rendering
- ✅ Computed values
🎨 LumaAnimate - Animation Engine
Quick Start
<script src="luma-animate.min.js"></script>
<script>
// Basic animation
LumaAnimate.to('.box', {
transform: { translateX: 200, rotate: 360 },
opacity: 0.5
}, {
duration: 1000,
easing: 'easeOutCubic'
});
</script>Core API
LumaAnimate.to(element, properties, options)
Animates element to target properties.
LumaAnimate.to('.box', {
transform: { translateY: 100, scale: 1.2 },
backgroundColor: '#ff6b6b',
opacity: 1
}, {
duration: 800,
delay: 200,
easing: 'easeOutBack',
onComplete: () => console.log('Done!')
});LumaAnimate.from(element, properties, options)
Animates element from specified properties to current state.
LumaAnimate.from('.box', {
opacity: 0,
transform: { translateY: -50 }
}, {
duration: 600,
easing: 'easeOutCubic'
});LumaAnimate.fromTo(element, from, to, options)
Animates element from specific start to end values.
LumaAnimate.fromTo('.box',
{ opacity: 0, transform: { scale: 0.5 } },
{ opacity: 1, transform: { scale: 1 } },
{ duration: 800, easing: 'easeOutBack' }
);Transform Properties
LumaAnimate.to('.element', {
transform: {
translateX: 100, // Move horizontally
translateY: 50, // Move vertically
rotate: 45, // Rotate in degrees
rotateX: 30, // 3D rotate on X axis
rotateY: 45, // 3D rotate on Y axis
scale: 1.5, // Scale uniformly
scaleX: 1.2, // Scale horizontally
scaleY: 0.8, // Scale vertically
skewX: 10, // Skew horizontally
skewY: 5 // Skew vertically
}
}, { duration: 1000 });Easing Functions (30+)
// Basic
'linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad'
// Cubic
'easeInCubic', 'easeOutCubic', 'easeInOutCubic'
// Quartic
'easeInQuart', 'easeOutQuart', 'easeInOutQuart'
// Quintic
'easeInQuint', 'easeOutQuint'
// Sine
'easeInSine', 'easeOutSine', 'easeInOutSine'
// Expo
'easeInExpo', 'easeOutExpo', 'easeInOutExpo'
// Circ
'easeInCirc', 'easeOutCirc', 'easeInOutCirc'
// Back
'easeInBack', 'easeOutBack', 'easeInOutBack'
// Elastic
'elastic', 'easeInElastic', 'easeOutElastic', 'easeInOutElastic'
// Bounce
'bounce', 'easeInBounce', 'easeOutBounce', 'easeInOutBounce'
// Spring Physics
LumaAnimate.easing.spring(tension, friction)Timeline Sequencing
Orchestrate complex animation sequences:
const tl = LumaAnimate.timeline();
tl
.to('.box1', { opacity: 1 }, { duration: 500 })
.to('.box2', { opacity: 1 }, { duration: 500 }) // After box1
.to('.box3', { opacity: 1 }, { duration: 500, position: '<' }) // Same time as box2
.addLabel('middle')
.to('.box4', { transform: { scale: 2 } }, { duration: 800, position: 'middle+200' })
.play();
// Control timeline
tl.pause();
tl.resume();
tl.restart();
tl.seek(0.5); // Jump to 50%Stagger Animations
Animate multiple elements with sequential delays:
const boxes = document.querySelectorAll('.box');
LumaAnimate.stagger(boxes, {
opacity: 1,
transform: { translateY: 0 }
}, {
duration: 600,
stagger: 100, // 100ms delay between each
easing: 'easeOutBack'
});Scroll Triggers
Trigger animations on scroll:
LumaAnimate.scrollTrigger('.element', {
animation: LumaAnimate.to('.element', {
opacity: 1,
transform: { translateY: 0 }
}, { duration: 800 }),
once: true, // Only trigger once
onEnter: (el) => console.log('Entered viewport'),
onLeave: (el) => console.log('Left viewport')
});Parallax Effects
LumaAnimate.parallax('.background', {
speed: 0.5, // Slower than scroll
direction: 'vertical' // or 'horizontal'
});Color Morphing
LumaAnimate.to('.box', {
backgroundColor: '#ff6b6b', // Hex colors
color: 'rgb(100, 200, 255)', // RGB colors
borderColor: '#00ff00'
}, { duration: 1000 });Preset Animations (20+)
// Fade
LumaAnimate.animate('.box', 'fadeIn');
LumaAnimate.animate('.box', 'fadeOut');
// Slide
LumaAnimate.animate('.box', 'slideInLeft');
LumaAnimate.animate('.box', 'slideInRight');
LumaAnimate.animate('.box', 'slideInUp');
LumaAnimate.animate('.box', 'slideInDown');
// Zoom
LumaAnimate.animate('.box', 'zoomIn');
LumaAnimate.animate('.box', 'zoomOut');
// Rotate
LumaAnimate.animate('.box', 'rotateIn');
LumaAnimate.animate('.box', 'rotateOut');
// Flip
LumaAnimate.animate('.box', 'flipInX');
LumaAnimate.animate('.box', 'flipInY');
// Attention Seekers
LumaAnimate.animate('.box', 'bounce');
LumaAnimate.animate('.box', 'pulse');
LumaAnimate.animate('.box', 'shake');
LumaAnimate.animate('.box', 'swing');
LumaAnimate.animate('.box', 'rubberBand');
LumaAnimate.animate('.box', 'heartBeat');Advanced Controls
const anim = LumaAnimate.to('.box', {
transform: { translateX: 200 }
}, {
duration: 2000,
repeat: 2, // Repeat 2 times
yoyo: true, // Reverse on repeat
repeatDelay: 500, // Delay between repeats
onStart: () => console.log('Started'),
onUpdate: (progress) => console.log('Progress:', progress),
onComplete: () => console.log('Done')
});
// Control methods
anim.pause();
anim.resume();
anim.reverse();
anim.restart();
anim.seek(0.5); // Jump to 50%
anim.kill(); // Stop and cleanupSpring Physics
Natural physics-based motion:
LumaAnimate.to('.box', {
transform: { translateY: -200 }
}, {
duration: 2000,
easing: LumaAnimate.easing.spring(170, 26) // tension, friction
});
// Presets
const softSpring = LumaAnimate.easing.spring(80, 20);
const stiffSpring = LumaAnimate.easing.spring(300, 30);SVG Path Animation
<svg>
<path id="myPath" d="M 0,0 Q 100,100 200,0" />
</svg>
<div class="follower"></div>
<script>
LumaAnimate.pathAnimation('.follower', '#myPath', {
duration: 3000,
rotate: true, // Rotate along path
easing: 'easeInOutCubic'
}).start();
</script>Performance Tips
- Use transforms instead of position properties for better performance
- Batch animations with timelines or stagger
- Use
will-changeCSS for elements that will animate - Kill animations when no longer needed
- Prefer hardware-accelerated properties: transform, opacity
🌟 Luma3D - 3D Engine
Quick Start
<canvas id="canvas"></canvas>
<script src="luma-3d.min.js"></script>
<script>
// Setup
const canvas = document.getElementById('canvas');
const scene = new Luma3D.Scene();
const camera = new Luma3D.PerspectiveCamera(75, canvas.width / canvas.height, 0.1, 1000);
const renderer = new Luma3D.WebGLRenderer({ canvas });
// Create 3D objects
const geometry = new Luma3D.BoxGeometry(1, 1, 1);
const material = new Luma3D.Material({ color: [1, 0.3, 0.3] });
const cube = new Luma3D.Mesh(geometry, material);
scene.add(cube);
// Camera position
camera.position = [0, 0, 5];
// Animation loop
function animate() {
cube.rotation[1] += 0.01;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
</script>Core API
Scene
const scene = new Luma3D.Scene();
scene.add(mesh); // Add object to scene
scene.remove(mesh); // Remove object
// Lighting
scene.lights.ambient.color = [0.3, 0.3, 0.4];
scene.lights.directional.position = [5, 10, 7];
scene.lights.directional.color = [1.2, 1.2, 1.0];Camera
const camera = new Luma3D.PerspectiveCamera(
fov = 75, // Field of view in degrees
aspect = 1.6, // Aspect ratio
near = 0.1, // Near clipping plane
far = 1000 // Far clipping plane
);
camera.position = [0, 5, 10];
camera.lookAt(0, 0, 0);Geometries
// Box
const boxGeo = new Luma3D.BoxGeometry(width, height, depth);
// Sphere
const sphereGeo = new Luma3D.SphereGeometry(
radius = 1,
widthSegments = 32,
heightSegments = 16
);
// Plane
const planeGeo = new Luma3D.PlaneGeometry(width, height);Materials
const material = new Luma3D.Material({
color: [1, 0.5, 0.2], // RGB values 0-1
wireframe: false, // Render as wireframe
shaderType: 'phong' // 'basic' or 'phong'
});Mesh
const mesh = new Luma3D.Mesh(geometry, material);
// Transform
mesh.position = [x, y, z];
mesh.rotation = [rx, ry, rz]; // Radians
mesh.scale = [sx, sy, sz];
mesh.visible = true;Renderer
const renderer = new Luma3D.WebGLRenderer({
canvas: canvasElement
});
renderer.render(scene, camera); // Render frame
renderer.setSize(width, height); // Update sizeOrbit Controls
const controls = new Luma3D.OrbitControls(camera, canvas);
controls.autoRotate = true;
controls.autoRotateSpeed = 0.01;
controls.update(); // Call in animation loopComplete Example
<!DOCTYPE html>
<html>
<head>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="luma-3d.min.js"></script>
<script>
const canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Setup scene
const scene = new Luma3D.Scene();
const camera = new Luma3D.PerspectiveCamera(
75,
canvas.width / canvas.height,
0.1,
1000
);
const renderer = new Luma3D.WebGLRenderer({ canvas });
const controls = new Luma3D.OrbitControls(camera, canvas);
// Create animated objects
const boxGeo = new Luma3D.BoxGeometry(1, 1, 1);
const sphereGeo = new Luma3D.SphereGeometry(0.7, 32, 16);
// Central rotating cube
const cube = new Luma3D.Mesh(
boxGeo,
new Luma3D.Material({ color: [1, 0.3, 0.3] })
);
cube.position = [-1.5, 0, 0];
scene.add(cube);
// Orbiting sphere
const sphere = new Luma3D.Mesh(
sphereGeo,
new Luma3D.Material({ color: [0.3, 0.5, 1] })
);
scene.add(sphere);
// Ground plane
const plane = new Luma3D.Mesh(
new Luma3D.PlaneGeometry(10, 10),
new Luma3D.Material({ color: [0.3, 0.3, 0.3] })
);
plane.rotation = [Math.PI / 2, 0, 0];
plane.position = [0, -2, 0];
scene.add(plane);
// Animation loop
let time = 0;
function animate() {
time += 0.016;
// Rotate cube
cube.rotation[1] += 0.01;
// Orbit sphere
sphere.position = [
Math.cos(time) * 3,
Math.sin(time * 2) * 0.5,
Math.sin(time) * 3
];
sphere.rotation[0] += 0.02;
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
// Handle resize
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
camera.aspect = canvas.width / canvas.height;
renderer.setSize(canvas.width, canvas.height);
});
</script>
</body>
</html>Features
- ⚡ Lightweight: ~18KB minified (vs Three.js ~580KB)
- 🎨 Phong Lighting: Realistic shading with ambient + directional lights
- 📷 Camera Controls: Built-in orbit controls with mouse/scroll
- 📦 3D Geometries: Box, Sphere, Plane (more coming)
- 🖄 Matrix Math: Full 3D transformations
- 🎮 Interactive: Drag to rotate, scroll to zoom
- 🖥 WebGL: Hardware-accelerated rendering
🛣️ Roadmap
| Step | Feature | Status | |------|---------|--------| | 1 | Core Reactivity | ✅ Complete | | 2 | Animation Engine | ✅ Complete | | 3 | 3D Engine (WebGL) | ✅ Complete | | 4 | Full Interactive 3D Website Demo | 📅 Next |
🧪 Technical Details
Reactivity System
LumaJS uses fine-grained reactivity with Proxy-based tracking:
- Track: When a reactive property is accessed during an effect, it's tracked as a dependency
- Trigger: When a property changes, all dependent effects re-run
- Cleanup: Effects clean up old dependencies before re-running
This approach is similar to Vue 3's reactivity system but optimized for minimal bundle size.
Bundle Size
- LumaCore: ~6 KB minified + gzipped
- Compare: React (~40 KB) + GSAP (~30 KB) + Three.js (~100 KB) = 170 KB
Browser Support
- Chrome 49+
- Firefox 18+
- Safari 10+
- Edge 12+
Requires Proxy support (all modern browsers).
🤝 Contributing
LumaJS is in active development. Contributions welcome!
Immediate Priorities
- [ ] Add
l-fordirective for list rendering - [ ] Add
l-ifdirective for conditional rendering (vsl-show) - [ ] Optimize batch updates for performance
- [ ] Add TypeScript definitions
- [ ] Build npm package
📄 License
MIT License – feel free to use in any project!
🌟 Why LumaJS?
| Challenge | Current Solution | LumaJS Solution |
|-----------|------------------|-----------------|
| Reactive UI | React (40 KB) | Built-in (~6 KB) |
| Animations | GSAP/Anime.js (30 KB) | Built-in (Step 2) |
| 3D Graphics | Three.js (100 KB) | Built-in (Step 3) |
| Setup Complexity | Multiple configs, bundlers | Single <script> tag |
| Learning Curve | JSX, hooks, separate libs | Simple directives |
One library. Zero setup. Infinite possibilities.
Next: Step 2 will add LumaAnimate for scroll triggers, hover effects, and micro-interactions!
