Skip to content

Conversation

@mrdoob
Copy link
Owner

@mrdoob mrdoob commented Dec 13, 2025

Description

The distance parameter was redundant - it can be derived from the physically-based intensity and decay values. This simplifies the API and ensures consistency between visual range and actual light falloff.

@mrdoob mrdoob added this to the r183 milestone Dec 13, 2025
@github-actions
Copy link

github-actions bot commented Dec 13, 2025

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 355.12
84.44
355.12
84.44
+0 B
+0 B
WebGPU 616.54
171.12
616.54
171.12
+0 B
+0 B
WebGPU Nodes 615.14
170.85
615.14
170.85
+0 B
+0 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 487.3
119.32
487.3
119.32
+0 B
+0 B
WebGPU 687.29
186.66
687.72
186.75
+426 B
+90 B
WebGPU Nodes 637.13
173.88
637.56
173.97
+426 B
+88 B

@mrdoob
Copy link
Owner Author

mrdoob commented Dec 13, 2025

Improved PointLightHelper while I was at it.

Screen.Recording.2025-12-13.at.17.16.29.mov

@mrdoob
Copy link
Owner Author

mrdoob commented Dec 13, 2025

Seems like distance was yet another parameter that was affecting PBR accuracy.
The fall-off in RoomEnvironment was not correct and that "leaked" into all the examples that used it.

@donmccurdy
Copy link
Collaborator

donmccurdy commented Dec 18, 2025

Note that this will mean dropping support for the range property in glTF's KHR_lights_punctual. From the spec, its purpose was:

Hint defining a distance cutoff at which the light's intensity may be considered to have reached zero. ... When undefined, range is assumed to be infinite.

This can be useful to cull geometry that a light may not visibly affect, potentially having a significant positive impact on rendering performance. It is required that, when given a non-zero value, rendering engines ignore the light beyond this range.

That said, I don't know if it's applied in this way by three.js users. If we prefer to not support this field in the glTF extension spec, that's OK with me.

@diffray-bot
Copy link

Changes Summary

This PR removes the redundant distance parameter from PointLight and SpotLight by making it a computed read-only getter based on intensity and decay values. This simplifies the API and ensures the visual range is physically consistent with the light's falloff behavior. All examples and documentation are updated accordingly.

Type: bugfix

Components Affected: PointLight, SpotLight, PointLightHelper, ObjectLoader, Editor, Examples (webgl and webgpu), Manual documentation

Files Changed
File Summary Change Impact
src/lights/PointLight.js Converted distance from stored property to computed getter/setter. Distance now calculated as Math.pow(intensity/0.01, 1/decay), with setter issuing deprecation warning. ✏️ 🔴
src/lights/SpotLight.js Converted distance from stored property to computed getter/setter using same formula as PointLight, with deprecation warning for setter. ✏️ 🔴
src/helpers/PointLightHelper.js Updated range circle visualization to use computed distance getter; now dynamically reflects light range based on intensity and decay. ✏️ 🟡
src/loaders/ObjectLoader.js Modified light instantiation to pass 0 for distance parameter instead of data.distance, ensuring backward compatibility with old scene files. ✏️ 🟡
test/unit/src/lights/PointLight.tests.js Updated test to pass 0 for distance parameter in constructor call. ✏️ 🟢
test/unit/src/lights/SpotLight.tests.js Updated tests to accommodate distance as computed property. ✏️ 🟢
examples/* (26+ files) Updated example code to remove distance parameter or pass 0, and adjusted intensity values where needed to maintain visual consistency. ✏️ 🟢
editor/js/*.js Removed distance property handling from light editing UI and serialization. ✏️ 🟡
manual/* (5 language versions) Updated documentation to clarify that distance is now computed from intensity and decay. ✏️ 🟢
Architecture Impact
  • New Patterns: Computed property pattern - distance transformed from stored field to read-only derived property
  • Coupling: Increased coupling between distance and decay properties - distance calculation now depends on decay value. Light range visualization now tightly coupled to intensity/decay formula.
  • Breaking Changes: Serialization change: distance no longer serialized in toJSON() for PointLight and SpotLight, Stored property removal: distance is no longer a settable instance property (setter logs warning), ObjectLoader behavior: old scenes with distance values will ignore the distance parameter, API incompatibility: code setting light.distance will only trigger warnings, not update any internal state

Risk Areas: Backward compatibility: Scenes saved with explicit distance values will lose that information when loaded, API contract violation: distance property setter exists but doesn't persist the value (silent behavior change), Physics correctness assumption: The formula Math.pow(intensity/0.01, 1/decay) assumes a specific light falloff model that may not apply universally, Editor compatibility: Editor changes suggest UI updates may not be complete across all use cases, Example parameter adjustments: intensity values doubled in some examples (200→400) which alters visual appearance, FBXLoader and other loaders: Changes to light instantiation may affect import behavior from external formats

Suggestions
  • Ensure deprecation warnings are clearly documented in migration guide
  • Add version notes to changelog explaining the breaking change and how to update existing scenes
  • Verify that intensity values adjusted in examples (e.g., 200→400) are correctly recalibrated for visual consistency
  • Consider adding a static utility method to compute the old distance value from intensity/decay for migration helpers
  • Test serialization/deserialization round-trips to ensure no data is lost unexpectedly
  • Document the physics assumptions behind the distance formula (1% intensity threshold) in code comments

Full review in progress... | Powered by diffray

if ( camera.isPerspectiveCamera ) {

const fov = camera.fov * ( Math.PI / 180 );
const scale = distance * Math.tan( fov / 2 ) * this.size * 0.04;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM - Magic number 0.04 without explanation
Agent: quality

Category: quality

Description:
The constant 0.04 is used in a scale calculation without explanation. This appears to be a tuning parameter for screen-space size calculation.

Suggestion:
Extract to a named constant at module level with a descriptive name like 'SCREEN_SPACE_SCALE_FACTOR' and document its purpose.

Confidence: 70%
Rule: qual_magic_numbers_js
Review ID: d45dfd15-2438-493b-ae82-0d50672f888b
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

'bottom': objectBottomRow,
'near': objectNearRow,
'far': objectFarRow,
'intensity': objectIntensityRow,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 LOW - console.warn used for exception logging
Agent: quality

Category: quality

Description:
Production code uses console.warn to log exceptions. Should use structured logging instead.

Suggestion:
Replace console.warn with a project-level logging utility or consider if this logging is needed.

Confidence: 60%
Rule: quality_avoid_console_in_production
Review ID: d45dfd15-2438-493b-ae82-0d50672f888b
Rate it 👍 or 👎 to improve future reviews | Powered by diffray


/**
* Constructs a new spot light.
*

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 LOW - Typo in JSDoc code example
Agent: react

Category: docs

Description:
JSDoc code example contains extra 's' character: 'spotLight.shadow.camera.fov = 30;s' should be 'spotLight.shadow.camera.fov = 30;'

Suggestion:
Remove the trailing 's' character from line 22.

Confidence: 100%
Rule: ts_always_use_jsdoc_for_documentation
Review ID: d45dfd15-2438-493b-ae82-0d50672f888b
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Comment on lines 1 to 46
import { PointLightHelper } from '../../../../src/helpers/PointLightHelper.js';

import { Mesh } from '../../../../src/objects/Mesh.js';
import { PointLight } from '../../../../src/lights/PointLight.js';

export default QUnit.module( 'Helpers', () => {

QUnit.module( 'PointLightHelper', () => {

const parameters = {
sphereSize: 1,
size: 1,
color: 0xaaaaaa,
intensity: 0.5,
distance: 100,
decay: 2
};

// INHERITANCE
QUnit.test( 'Extending', ( assert ) => {

const light = new PointLight( parameters.color );
const object = new PointLightHelper( light, parameters.sphereSize, parameters.color );
assert.strictEqual(
object instanceof Mesh, true,
'PointLightHelper extends from Mesh'
);

} );

// INSTANCING
QUnit.test( 'Instancing', ( assert ) => {

const light = new PointLight( parameters.color );
const object = new PointLightHelper( light, parameters.sphereSize, parameters.color );
const object = new PointLightHelper( light, parameters.size );
assert.ok( object, 'Can instantiate a PointLightHelper.' );

} );

// PROPERTIES
QUnit.test( 'type', ( assert ) => {

const light = new PointLight( parameters.color );
const object = new PointLightHelper( light, parameters.sphereSize, parameters.color );
const object = new PointLightHelper( light, parameters.size );
assert.ok(
object.type === 'PointLightHelper',
'PointLightHelper.type should be PointLightHelper'
);

} );

// PUBLIC
QUnit.test( 'dispose', ( assert ) => {

assert.expect( 0 );

const light = new PointLight( parameters.color );
const object = new PointLightHelper( light, parameters.sphereSize, parameters.color );
const object = new PointLightHelper( light, parameters.size );
object.dispose();

} );

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 HIGH - New PointLightHelper 'size' parameter lacks test coverage
Agent: testing

Category: quality

Description:
PointLightHelper constructor has size parameter (default=1) but tests don't verify the property is set correctly or test default behavior.

Suggestion:
Add tests: verify helper.size equals passed value, test default value when size omitted, test with different values (0, 2, etc.)

Confidence: 80%
Rule: test_new_parameter_coverage
Review ID: d45dfd15-2438-493b-ae82-0d50672f888b
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

let lights = undefined;
hooks.beforeEach( function () {

const parameters = {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 HIGH - PointLight distance getter lacks test coverage
Agent: testing

Category: quality

Description:
PointLight.distance is now a computed getter (lines 94-106) based on intensity/decay. Tests don't verify this behavior.

Suggestion:
Add test: create light with known intensity/decay, assert distance getter returns expected computed value. Also test distance setter shows deprecation warning.

Confidence: 85%
Rule: test_new_parameter_coverage
Review ID: d45dfd15-2438-493b-ae82-0d50672f888b
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

let lights = undefined;
hooks.beforeEach( function () {

const parameters = {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 HIGH - SpotLight distance getter lacks test coverage
Agent: testing

Category: quality

Description:
SpotLight.distance is now a computed getter (lines 151-163) based on intensity/decay. Tests don't verify this behavior.

Suggestion:
Add test: create light with known intensity/decay, assert distance getter returns expected computed value. Also test distance setter shows deprecation warning.

Confidence: 85%
Rule: test_new_parameter_coverage
Review ID: d45dfd15-2438-493b-ae82-0d50672f888b
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

@diffray-bot
Copy link

Review Summary

Free public review - Want AI code reviews on your PRs? Check out diffray.ai

Validated 42 issues: 8 kept, 34 filtered

Issues Found: 8

💬 See 6 individual line comment(s) for details.

📊 5 unique issue type(s) across 8 location(s)

📋 Full issue list (click to expand)

🟠 HIGH - New PointLightHelper 'size' parameter lacks test coverage (3 occurrences)

Agent: testing

Category: quality

📍 View all locations
File Description Suggestion Confidence
test/unit/src/helpers/PointLightHelper.tests.js:1-46 PointLightHelper constructor has size parameter (default=1) but tests don't verify the property is s... Add tests: verify helper.size equals passed value, test default value when size omitted, test with d... 80%
test/unit/src/lights/PointLight.tests.js:13 PointLight.distance is now a computed getter (lines 94-106) based on intensity/decay. Tests don't ve... Add test: create light with known intensity/decay, assert distance getter returns expected computed ... 85%
test/unit/src/lights/SpotLight.tests.js:13 SpotLight.distance is now a computed getter (lines 151-163) based on intensity/decay. Tests don't ve... Add test: create light with known intensity/decay, assert distance getter returns expected computed ... 85%

Rule: test_new_parameter_coverage


🟡 MEDIUM - Magic number 0.04 without explanation

Agent: quality

Category: quality

File: src/helpers/PointLightHelper.js:164

Description: The constant 0.04 is used in a scale calculation without explanation. This appears to be a tuning parameter for screen-space size calculation.

Suggestion: Extract to a named constant at module level with a descriptive name like 'SCREEN_SPACE_SCALE_FACTOR' and document its purpose.

Confidence: 70%

Rule: qual_magic_numbers_js


🟡 MEDIUM - Large block of commented-out code

Agent: refactoring

Category: quality

File: editor/js/Sidebar.Object.js:29-68

Description: 40-line commented code block with implementation logic (object actions UI with switch statements). Should either be removed or restored.

Suggestion: Remove the entire commented block (lines 29-68) unless it will be restored soon. Use version control for code history instead.

Confidence: 85%

Rule: quality_commented_code


🔵 LOW - console.warn used for exception logging (2 occurrences)

Agent: quality

Category: quality

📍 View all locations
File Description Suggestion Confidence
editor/js/Sidebar.Object.js:634 Production code uses console.warn to log exceptions. Should use structured logging instead. Replace console.warn with a project-level logging utility or consider if this logging is needed. 60%
editor/js/Sidebar.Object.js:860 Production code uses console.log to log JSON parsing errors. Should use console.error or structured ... Replace console.log with console.error or a project-level logging utility. 62%

Rule: quality_avoid_console_in_production


🔵 LOW - Typo in JSDoc code example

Agent: react

Category: docs

File: src/lights/SpotLight.js:31

Description: JSDoc code example contains extra 's' character: 'spotLight.shadow.camera.fov = 30;s' should be 'spotLight.shadow.camera.fov = 30;'

Suggestion: Remove the trailing 's' character from line 22.

Confidence: 100%

Rule: ts_always_use_jsdoc_for_documentation


ℹ️ 2 issue(s) outside PR diff (click to expand)

These issues were found in lines not modified in this PR.

🟡 MEDIUM - Large block of commented-out code

Agent: refactoring

Category: quality

File: editor/js/Sidebar.Object.js:29-68

Description: 40-line commented code block with implementation logic (object actions UI with switch statements). Should either be removed or restored.

Suggestion: Remove the entire commented block (lines 29-68) unless it will be restored soon. Use version control for code history instead.

Confidence: 85%

Rule: quality_commented_code


🔵 LOW - console.log used for error logging

Agent: quality

Category: quality

File: editor/js/Sidebar.Object.js:860

Description: Production code uses console.log to log JSON parsing errors. Should use console.error or structured logging.

Suggestion: Replace console.log with console.error or a project-level logging utility.

Confidence: 62%

Rule: quality_avoid_console_in_production



Review ID: d45dfd15-2438-493b-ae82-0d50672f888b
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants