# Documentation: Do UVs need to match with position attribute? Or index attribute?

**URL:** <https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408>\
**Category:** Site Feedback\
**Tags:** uv, indexed-geometry, uv-mapping, geometry\
**Created:** [March 30, 2025, 7:22pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408 "2025-03-30T19:22:16Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![trusktr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/trusktr/32/13726_2.png) [@trusktr](https://discourse.threejs.org/u/trusktr)\
**Post date:** [March 30, 2025, 7:22pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/1 "2025-03-30T19:22:16Z")

</div>

Unless I missed it, I didn’t see the docs describing the rules for UV attributes. For example, does the `uv` attribute need to match with `position` or with `index` (indexed geometry vs non-indexed geometry)?

---

<div class="post-metadata">

**Author:** ![Fyrestar](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/fyrestar/32/52793_2.png) [@Fyrestar](https://discourse.threejs.org/u/Fyrestar)\
**Post date:** [March 30, 2025, 7:25pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/2 "2025-03-30T19:25:49Z")

</div>

For each vertex there needs to be a set (matching item count) of any vertex attribute used.

---

<div class="post-metadata">

**Author:** ![trusktr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/trusktr/32/13726_2.png) [@trusktr](https://discourse.threejs.org/u/trusktr)\
**Post date:** [March 30, 2025, 7:31pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/3 "2025-03-30T19:31:00Z")

</div>

So

```js
// always
geometry.attribute.position.count === geometry.attribute.uv.count

```

regardless of indices.

I would imagine that if I were making a framework like threejs, I could make it be aligned with the indices instead (or make an option for either behavior). This would make it easy to, for example, give each triangle a different piece of texture individually, rather than being locked to the vertices.

---

<div class="post-metadata">

**Author:** ![PavelBoytchev](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/pavelboytchev/32/38639_2.png) [@PavelBoytchev](https://discourse.threejs.org/u/PavelBoytchev)\
**Post date:** [March 30, 2025, 7:58pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/4 "2025-03-30T19:58:29Z")

</div>

This index-ing was born in the early days of OpenGL and then went to WebGL. It is not a limitation of Three.js per se. Back then, there were two ways to make a draw call – either non-indexed (`glDrawArrays`) or indexed (`glDrawElements`). If the indexed call is used, all attributes had to be indexed (positions, colors, uvs, etc.) Indexing is not as good at is sounds, because even if two vertices share the same position, but different colors or uvs, they cannot use the same index.

As for `geometry.attribute.*.count` I’m not quite sure, as I have some vague recollection that it was possible to store more values in the attribute arrays than are actually used in a draw call.

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [March 30, 2025, 8:08pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/5 "2025-03-30T20:08:11Z")

</div>

I started writing down a whole treatise of the old Geometry vs BufferGeometry, and then instead, just asked GPT to summarize the issue:

Absolutely. The old `THREE.Geometry` class in Three.js was deprecated in favor of `THREE.BufferGeometry` for **performance, memory efficiency, and GPU compatibility**. Here’s a breakdown of why:

* * *

### 🧱 `THREE.Geometry` (Old)

- **Structure** : Stored data in high-level JS arrays (`geometry.vertices`, `geometry.faces`, etc.).
- **Ease of use** : Very beginner-friendly; you could push vertices and faces and see results immediately.
- **Internals** : Required conversion to `BufferGeometry` under the hood before rendering (`geometry.toBufferGeometry()` was common).
- **Downsides** :
  - Slow to upload to GPU.
  - Inefficient memory layout (objects in arrays, not typed arrays).
  - Frequent garbage collection hits.
  - Difficult to parallelize or batch.

* * *

### 🚀 `THREE.BufferGeometry` (New)

- **Structure** : Stores data in raw **typed arrays** (`Float32Array`, `Uint16Array`, etc.).
- **GPU-friendly** : Directly maps to WebGL buffer objects—fast upload, low overhead.
- **Efficient** : Smaller memory footprint, better for batching and instancing.
- **Lower-level** : Requires more manual setup—positions, indices, normals, uvs must be added explicitly.
- **Flexible** : You can define custom attributes and write your own shaders that read them.

* * *

### 🧠 Why the shift?

1. **Performance** : `BufferGeometry` is much faster, especially for complex or dynamic scenes.
2. **WebGL Alignment** : It fits WebGL’s native data model better—typed arrays and buffer objects.
3. **Maintainability** : Avoids maintaining two parallel systems.
4. **Modern Practices** : Encourages devs to work with raw data, which is essential for advanced rendering and effects.

* * *

### TL;DR

| Feature | `Geometry` | `BufferGeometry` |
| --- | --- | --- |
| Ease of Use | 👍 Easy to use | 👎 More setup required |
| Performance | 👎 Slower | 👍 Faster |
| GPU Upload | 👎 Needs conversion | 👍 Native format |
| Memory Efficiency | 👎 Poor (object-heavy) | 👍 Great (typed arrays) |
| Advanced Features | 👎 Limited | 👍 Supports custom attrs |
| Deprecated | ✅ Yes | ❌ No (current standard) |

* * *

If you’re building anything in modern Three.js, `BufferGeometry` is the way to go—especially for anything dynamic, GPU-intensive, or procedural. Want a simple helper or abstraction to make `BufferGeometry` easier to use like `Geometry` used to be? I can show you that too.

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [March 30, 2025, 8:13pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/6 "2025-03-30T20:13:43Z")

</div>

> One of the major takeaways here is that the old `Geometry` class had a **fixed, rigid vertex layout**. It stored positions, normals, UVs, etc., as separate high-level arrays of objects (like `Vector3`, `Vector2`, etc.). This made it easy to work with but offered **no control over how vertex data was structured in memory**. Every geometry essentially followed the same layout, which wasn’t optimal for performance.
> 
> In contrast, `BufferGeometry` gives you **complete control over your vertex attribute layout**. You can:
> 
> - Store each attribute (position, normal, uv, etc.) in **separate buffers** (default),
> - Or pack everything into a **single interleaved buffer** , where all vertex attributes are tightly packed per vertex (e.g., position → normal → uv → …).
> 
> This interleaved format is often **more GPU-friendly** because it improves **data locality**. Instead of the GPU having to fetch data from multiple different buffers for each attribute (potentially triggering multiple memory reads per vertex), it can fetch **all the vertex attributes in a single read** from one buffer. This reduces cache misses and improves memory throughput — which is critical in real-time graphics and especially when drawing many vertices.
> 
> So beyond just performance or memory efficiency, `BufferGeometry` opens up **low-level optimizations** that weren’t possible before — letting you tailor your geometry to best suit how the GPU wants to consume data.

---

<div class="post-metadata">

**Author:** ![trusktr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/trusktr/32/13726_2.png) [@trusktr](https://discourse.threejs.org/u/trusktr)\
**Post date:** [March 30, 2025, 8:27pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/7 "2025-03-30T20:27:58Z")

</div>

> [@PavelBoytchev](#):
>
> it was possible to store more values in the attribute arrays than are actually used in a draw call.

I’m making the assumption here that it has the minimum number of values (no extra), with default draw range of everything. This is the most common scenario, especially for high level users who don’t know how to modify those details.

---

<div class="post-metadata">

**Author:** ![trusktr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/trusktr/32/13726_2.png) [@trusktr](https://discourse.threejs.org/u/trusktr)\
**Post date:** [March 30, 2025, 8:28pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/8 "2025-03-30T20:28:51Z")

</div>

This has absolutely nothing to do with the topic of the thread. lol Did you respond to the wrong thread maybe?

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [March 30, 2025, 8:30pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/9 "2025-03-30T20:30:32Z")

</div>

> [@trusktr](#):
>
> I would imagine that if I were making a framework like threejs, I could make it be aligned with the indices instead (or make an option for either behavior). This would make it easy to, for example, give each triangle a different piece of texture individually, rather than being locked to the vertices.

I’m explaining why you might not take the approach you outlined.

(Your approach being similar to what the original Geometry class offered.. i.e. “give each triangle a separate piece of texture by default” )

Trying to summarize the years of discussion that led to the current system.

Apologies if this isn’t exactly what you were discussing.. but I thought it relevant to understanding the current design.

---

<div class="post-metadata">

**Author:** ![trusktr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/trusktr/32/13726_2.png) [@trusktr](https://discourse.threejs.org/u/trusktr)\
**Post date:** [March 30, 2025, 8:58pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/10 "2025-03-30T20:58:04Z")

</div>

Yeah, that’s not really relevant. This is only discussing current Three.js: do uvs need to match with position, or with index (and getting that documented). That way people who define a `uv` attribute know what to define based on whether `geometry.index` exists or not.

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [March 30, 2025, 9:16pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/11 "2025-03-30T21:16:36Z")

</div>

yes for non-indexed geometreis, position.count normal.count, and uv.count should all be the same..

for indexed geometries, it might be possible to have different counts.. as long as none of the indices are out of range of the smallest count in the set.  
🙂

---

<div class="post-metadata">

**Author:** ![Attila\_Schroeder](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/attila_schroeder/32/36097_2.png) [@Attila\_Schroeder](https://discourse.threejs.org/u/Attila_Schroeder)\
**Post date:** [March 30, 2025, 10:58pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/12 "2025-03-30T22:58:39Z")

</div>

With BufferGeometry you have complete freedom.

---

<div class="post-metadata">

**Author:** ![dubois](https://avatars.discourse-cdn.com/v4/letter/d/e9c0ed/32.png) [@dubois](https://discourse.threejs.org/u/dubois)\
**Post date:** [March 31, 2025, 4:10pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/13 "2025-03-31T16:10:13Z")

</div>

All attributes need to have the same count.

---

<div class="post-metadata">

**Author:** ![trusktr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/trusktr/32/13726_2.png) [@trusktr](https://discourse.threejs.org/u/trusktr)\
**Post date:** [March 31, 2025, 6:48pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/14 "2025-03-31T18:48:18Z")

</div>

> [@dubois](#):
>
> All attributes need to have the same count.

`index` can have a different `count` than `position`.

> [@manthrax](#):
>
> for indexed geometries, it might be possible to have different counts

For example? (with regards to UVs)

---

<div class="post-metadata">

**Author:** ![donmccurdy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/donmccurdy/32/33209_2.png) [@donmccurdy](https://discourse.threejs.org/u/donmccurdy)\
**Post date:** [March 31, 2025, 6:56pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/15 "2025-03-31T18:56:08Z")

</div>

Important distinction here:

- **vertex attributes:** if a mesh has N vertices, each vertex attribute must contain N items or vectors
- **instance attributes:** if a mesh is drawn M times with instancing, each instance attribute must contain M items or vectors
- **indices:** may have any length valid for the draw mode (e.g. for triangles, must be a multiple of 3). The index defines what triangles/lines/points are to be drawn, using the vertices available in the geometry.

Strictly I think indices are not usually referred to as an “attribute”. If indices are omitted, implicitly WebGL draws as if you had provided indices `1, 2, 3, 4, 5, 6, ... N`. This is usually less efficient because no vertices are reused, and the vertex cache does nothing.

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [March 31, 2025, 7:10pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/16 "2025-03-31T19:10:40Z")

</div>

For example.. this works, and doesn’t generate errors afaik:

```javascript
let plane = new THREE.Mesh(new THREE.PlaneGeometry(100,100),new THREE.MeshStandardMaterial());
plane.geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(51),2));
console.log(plane.geometry.attributes.uv.count);

```

(This logs a count of 25.5 😃 )  
So.. a uv buffer arbitrarily larger than position or normal, and oddly sized.

Why one would need this is somewhat mysterious, but there are schemes (like batching) where these kinds of shenanigans might make sense… like sharing a common attribute amongst multiple geometries or smth.

@donmccurdy :

> - **vertex attributes:** if a mesh has N vertices, each vertex attribute must contain N items or vectors

would this more accurately be “must contain At Least N items or vectors?”

.. as, the above “works”.. (even with an odd sized array!?)

---

<div class="post-metadata">

**Author:** ![donmccurdy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/donmccurdy/32/33209_2.png) [@donmccurdy](https://discourse.threejs.org/u/donmccurdy)\
**Post date:** [March 31, 2025, 7:29pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/17 "2025-03-31T19:29:41Z")

</div>

> [@manthrax](#):
>
> would this more accurately be “must contain At Least N items or vectors?”

Ah true! In WebGL generally that would be useful to upload data you’ll use for other draw calls later, but in three.js — where different geometries do not really share attributes at the GPU level, and geometries cannot be resized other than via drawRange — I can’t think of a real use case for mismatched attribute sizes.

Technically there’s also the concept of [constant attributes](https://www.khronos.org/opengl/wiki/Vertex_Specification#Non-array_attribute_values), having the same value for all vertices. But three.js does not expose that, it doesn’t seem like they have any clear benefits over uniforms, and the OpenGL spec even says “It is not recommended that you use these.” 🙂

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [March 31, 2025, 8:19pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/18 "2025-03-31T20:19:09Z")

</div>

> different geometries do not really share attributes at the GPU level

It’s allowed though.

You can have 2 planes, and set their position attribute to the same attribute.. later on update the single attributes array, and both will reflect the change.

…

r.e. constant attributes.. I’ve made use of these in the past.  
Agree that they are probably undersupperted/tested though.. Ultimately driver implementations are the bane of all these fun/weird use cases. 😃

---

<div class="post-metadata">

**Author:** ![donmccurdy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/donmccurdy/32/33209_2.png) [@donmccurdy](https://discourse.threejs.org/u/donmccurdy)\
**Post date:** [March 31, 2025, 8:51pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/19 "2025-03-31T20:51:04Z")

</div>

Duplicated on the GPU, but allowed as far as I know, yep! I don’t expect _reusing_ a BufferAttribute would ever be prohibited. But I think mixing-and-matching with different attribute counts will likely run into bugs that would be viewed with some … skepticism … if reported on the bug tracker. So if that part works now, I do view it as more of an accident. 😉

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [March 31, 2025, 8:52pm UTC](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408/20 "2025-03-31T20:52:38Z")

</div>

Agree 100%. definitely whiskey/weed territory.

[Next page](https://discourse.threejs.org/t/documentation-do-uvs-need-to-match-with-position-attribute-or-index-attribute/80408.md?page=2)
